Cleanup, and clarify the handling of the various rectangles.
[wine.git] / dlls / comctl32 / listview.c
blobc9c5ada8c5c5c3c943b0d2773631455122ff794e
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 POINT ptPosition;
104 BOOL valid;
105 } LISTVIEW_ITEM;
107 typedef struct tagRANGE
109 INT lower;
110 INT upper;
111 } RANGE;
113 typedef struct tagLISTVIEW_INFO
115 HWND hwndSelf;
116 HBRUSH hBkBrush;
117 COLORREF clrBk;
118 COLORREF clrText;
119 COLORREF clrTextBk;
120 HIMAGELIST himlNormal;
121 HIMAGELIST himlSmall;
122 HIMAGELIST himlState;
123 BOOL bLButtonDown;
124 BOOL bRButtonDown;
125 INT nItemHeight;
126 INT nItemWidth;
127 HDPA hdpaSelectionRanges;
128 INT nSelectionMark;
129 BOOL bRemovingAllSelections;
130 INT nHotItem;
131 SHORT notifyFormat;
132 RECT rcList; /* This rectangle is really the window
133 * client rectangle possibly reduced by the
134 * horizontal scroll bar and/or header - see
135 * LISTVIEW_UpdateSize. This rectangle offset
136 * by the LISTVIEW_GetOrigin value is within
137 * the rcView rectangle */
138 RECT rcView; /* This rectangle contains all items -
139 * contructed in LISTVIEW_AlignTop and
140 * LISTVIEW_AlignLeft */
141 SIZE iconSize;
142 SIZE iconSpacing;
143 SIZE iconStateSize;
144 UINT uCallbackMask;
145 HWND hwndHeader;
146 HFONT hDefaultFont;
147 HCURSOR hHotCursor;
148 HFONT hFont;
149 INT ntmHeight; /* from GetTextMetrics from above font */
150 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
151 BOOL bFocus;
152 INT nFocusedItem;
153 RECT rcFocus;
154 DWORD dwStyle; /* the cached window GWL_STYLE */
155 DWORD dwLvExStyle; /* extended listview style */
156 HDPA hdpaItems;
157 UINT nItemCount;
158 PFNLVCOMPARE pfnCompare;
159 LPARAM lParamSort;
160 HWND hwndEdit;
161 BOOL bEditing;
162 WNDPROC EditWndProc;
163 INT nEditLabelItem;
164 DWORD dwHoverTime;
166 DWORD lastKeyPressTimestamp;
167 WPARAM charCode;
168 INT nSearchParamLength;
169 WCHAR szSearchParam[ MAX_PATH ];
170 BOOL bIsDrawing;
171 } LISTVIEW_INFO;
173 DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO, hwndSelf);
176 * constants
178 /* How many we debug buffer to allocate */
179 #define DEBUG_BUFFERS 20
180 /* The size of a single debug bbuffer */
181 #define DEBUG_BUFFER_SIZE 256
183 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
184 #define SB_INTERNAL -1
186 /* maximum size of a label */
187 #define DISP_TEXT_SIZE 512
189 /* padding for items in list and small icon display modes */
190 #define WIDTH_PADDING 12
192 /* padding for items in list, report and small icon display modes */
193 #define HEIGHT_PADDING 1
195 /* offset of items in report display mode */
196 #define REPORT_MARGINX 2
198 /* padding for icon in large icon display mode
199 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
200 * that HITTEST will see.
201 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
202 * ICON_TOP_PADDING - sum of the two above.
203 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
204 * LABEL_VERT_PADDING - between bottom of text and end of box
206 * ICON_LR_PADDING - additional width above icon size.
207 * ICON_LR_HALF - half of the above value
209 #define ICON_TOP_PADDING_NOTHITABLE 2
210 #define ICON_TOP_PADDING_HITABLE 2
211 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
212 #define ICON_BOTTOM_PADDING 4
213 #define LABEL_VERT_PADDING 7
214 #define ICON_LR_PADDING 16
215 #define ICON_LR_HALF (ICON_LR_PADDING/2)
217 /* default label width for items in list and small icon display modes */
218 #define DEFAULT_LABEL_WIDTH 40
220 /* default column width for items in list display mode */
221 #define DEFAULT_COLUMN_WIDTH 128
223 /* Size of "line" scroll for V & H scrolls */
224 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
226 /* Padding betwen image and label */
227 #define IMAGE_PADDING 2
229 /* Padding behind the label */
230 #define TRAILING_PADDING 5
232 /* Border for the icon caption */
233 #define CAPTION_BORDER 2
235 /* Standard DrawText flags for LISTVIEW_UpdateLargeItemLabelRect and LISTVIEW_DrawLargeItem */
236 #define LISTVIEW_DTFLAGS DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL
238 /* Dump the LISTVIEW_INFO structure to the debug channel */
239 #define LISTVIEW_DUMP(iP) do { \
240 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
241 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
242 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
243 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
244 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
245 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
246 (iP->bFocus) ? "true" : "false"); \
247 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
248 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
249 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
250 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
251 iP->hwndSelf, \
252 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
253 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
254 } while(0)
258 * forward declarations
260 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
261 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
262 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
263 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
264 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
265 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *);
266 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
267 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
268 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
269 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *, INT, LPRECT);
270 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
271 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
272 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
273 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
274 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
275 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
276 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, LONG, LONG);
277 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
278 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
279 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
280 static void LISTVIEW_UnsupportedStyles(LONG);
281 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
282 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
283 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
284 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
285 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *, WPARAM, LPARAM);
286 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
287 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
288 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
289 static BOOL LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *, int, RECT*);
290 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
291 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
292 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
293 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
294 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
295 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
297 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
298 #define KEY_DELAY 450
300 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
303 /******** Text handling functions *************************************/
305 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
306 * text string. The string may be ANSI or Unicode, in which case
307 * the boolean isW tells us the type of the string.
309 * The name of the function tell what type of strings it expects:
310 * W: Unicode, T: ANSI/Unicode - function of isW
313 static inline BOOL is_textW(LPCWSTR text)
315 return text != NULL && text != LPSTR_TEXTCALLBACKW;
318 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
320 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
321 return is_textW(text);
324 static inline int textlenT(LPCWSTR text, BOOL isW)
326 return !is_textT(text, isW) ? 0 :
327 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
330 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
332 if (isDestW)
333 if (isSrcW) lstrcpynW(dest, src, max);
334 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
335 else
336 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
337 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
340 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
342 LPWSTR wstr = (LPWSTR)text;
344 if (!isW && is_textT(text, isW))
346 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
347 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
348 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
350 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
351 return wstr;
354 static inline void textfreeT(LPWSTR wstr, BOOL isW)
356 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
360 * dest is a pointer to a Unicode string
361 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
363 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
365 BOOL bResult = TRUE;
367 if (src == LPSTR_TEXTCALLBACKW)
369 if (is_textW(*dest)) COMCTL32_Free(*dest);
370 *dest = LPSTR_TEXTCALLBACKW;
372 else
374 LPWSTR pszText = textdupTtoW(src, isW);
375 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
376 bResult = Str_SetPtrW(dest, pszText);
377 textfreeT(pszText, isW);
379 return bResult;
383 * compares a Unicode to a Unicode/ANSI text string
385 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
387 if (!aw) return bt ? -1 : 0;
388 if (!bt) return aw ? 1 : 0;
389 if (aw == LPSTR_TEXTCALLBACKW)
390 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
391 if (bt != LPSTR_TEXTCALLBACKW)
393 LPWSTR bw = textdupTtoW(bt, isW);
394 int r = bw ? lstrcmpW(aw, bw) : 1;
395 textfreeT(bw, isW);
396 return r;
399 return 1;
402 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
404 int res;
406 n = min(min(n, strlenW(s1)), strlenW(s2));
407 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
408 return res ? res - sizeof(WCHAR) : res;
411 /******** Debugging functions *****************************************/
413 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
415 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
416 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
419 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
421 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
422 n = min(textlenT(text, isW), n);
423 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
426 static char* debug_getbuf()
428 static int index = 0;
429 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
430 return buffers[index++ % DEBUG_BUFFERS];
433 static inline char* debugrect(const RECT* rect)
435 if (rect) {
436 char* buf = debug_getbuf();
437 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
438 rect->left, rect->top, rect->right, rect->bottom);
439 return buf;
440 } else return "(null)";
443 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
445 char* buf = debug_getbuf(), *text = buf;
446 int len, size = DEBUG_BUFFER_SIZE;
448 if (lpLVItem == NULL) return "(null)";
449 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
450 if (len == -1) goto end; buf += len; size -= len;
451 if (lpLVItem->mask & LVIF_STATE)
452 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
453 else len = 0;
454 if (len == -1) goto end; buf += len; size -= len;
455 if (lpLVItem->mask & LVIF_TEXT)
456 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
457 else len = 0;
458 if (len == -1) goto end; buf += len; size -= len;
459 if (lpLVItem->mask & LVIF_IMAGE)
460 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
461 else len = 0;
462 if (len == -1) goto end; buf += len; size -= len;
463 if (lpLVItem->mask & LVIF_PARAM)
464 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
465 else len = 0;
466 if (len == -1) goto end; buf += len; size -= len;
467 if (lpLVItem->mask & LVIF_INDENT)
468 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
469 else len = 0;
470 if (len == -1) goto end; buf += len; size -= len;
471 goto undo;
472 end:
473 buf = text + strlen(text);
474 undo:
475 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
476 return text;
479 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
481 char* buf = debug_getbuf(), *text = buf;
482 int len, size = DEBUG_BUFFER_SIZE;
484 if (lpColumn == NULL) return "(null)";
485 len = snprintf(buf, size, "{");
486 if (len == -1) goto end; buf += len; size -= len;
487 if (lpColumn->mask & LVCF_SUBITEM)
488 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
489 else len = 0;
490 if (len == -1) goto end; buf += len; size -= len;
491 if (lpColumn->mask & LVCF_FMT)
492 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
493 else len = 0;
494 if (len == -1) goto end; buf += len; size -= len;
495 if (lpColumn->mask & LVCF_WIDTH)
496 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
497 else len = 0;
498 if (len == -1) goto end; buf += len; size -= len;
499 if (lpColumn->mask & LVCF_TEXT)
500 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
501 else len = 0;
502 if (len == -1) goto end; buf += len; size -= len;
503 if (lpColumn->mask & LVCF_IMAGE)
504 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
505 else len = 0;
506 if (len == -1) goto end; buf += len; size -= len;
507 if (lpColumn->mask & LVCF_ORDER)
508 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
509 else len = 0;
510 if (len == -1) goto end; buf += len; size -= len;
511 goto undo;
512 end:
513 buf = text + strlen(text);
514 undo:
515 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
516 return text;
520 /******** Notification functions i************************************/
522 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
524 pnmh->hwndFrom = infoPtr->hwndSelf;
525 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
526 pnmh->code = code;
527 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
528 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
531 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
533 NMHDR nmh;
534 notify(infoPtr, LVN_ITEMACTIVATE, &nmh);
537 static inline BOOL notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
539 return notify(infoPtr, code, (LPNMHDR)plvnm);
542 static int get_ansi_notification(INT unicodeNotificationCode)
544 switch (unicodeNotificationCode)
546 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
547 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
548 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
549 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
550 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
551 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
553 ERR("unknown notification %x\n", unicodeNotificationCode);
554 return unicodeNotificationCode;
558 Send notification. depends on dispinfoW having same
559 structure as dispinfoA.
560 infoPtr : listview struct
561 notificationCode : *Unicode* notification code
562 pdi : dispinfo structure (can be unicode or ansi)
563 isW : TRUE if dispinfo is Unicode
565 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
567 BOOL bResult = FALSE;
568 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
569 INT realNotifCode;
570 INT cchTempBufMax = 0, savCchTextMax = 0;
571 LPWSTR pszTempBuf = NULL, savPszText = NULL;
573 TRACE("(code=%x, pdi=%p, isW=%d)\n", notificationCode, pdi, isW);
574 TRACE(" notifyFormat=%s\n",
575 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
576 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
577 if (infoPtr->notifyFormat == NFR_ANSI)
578 realNotifCode = get_ansi_notification(notificationCode);
579 else
580 realNotifCode = notificationCode;
582 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
584 if (isW && infoPtr->notifyFormat == NFR_ANSI)
585 convertToAnsi = TRUE;
586 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
587 convertToUnicode = TRUE;
590 if (convertToAnsi || convertToUnicode)
592 TRACE(" we have to convert the text to the correct format\n");
593 if (notificationCode != LVN_GETDISPINFOW)
594 { /* length of existing text */
595 cchTempBufMax = convertToUnicode ?
596 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
597 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
599 else
600 cchTempBufMax = pdi->item.cchTextMax;
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 TRACE(" text=%s\n", debugtext_t(pszTempBuf, convertToUnicode));
612 savCchTextMax = pdi->item.cchTextMax;
613 savPszText = pdi->item.pszText;
614 pdi->item.pszText = pszTempBuf;
615 pdi->item.cchTextMax = cchTempBufMax;
618 bResult = notify(infoPtr, realNotifCode, (LPNMHDR)pdi);
620 if (convertToUnicode || convertToAnsi)
621 { /* convert back result */
622 TRACE(" returned text=%s\n", debugtext_t(pdi->item.pszText, convertToUnicode));
623 if (convertToUnicode) /* note : pointer can be changed by app ! */
624 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
625 savCchTextMax, NULL, NULL);
626 else
627 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
628 savPszText, savCchTextMax);
629 pdi->item.pszText = savPszText; /* restores our buffer */
630 pdi->item.cchTextMax = savCchTextMax;
631 HeapFree(GetProcessHeap(), 0, pszTempBuf);
633 return bResult;
636 static inline void notify_odcachehint(LISTVIEW_INFO *infoPtr, INT iFrom, INT iTo)
638 NMLVCACHEHINT nmlv;
640 nmlv.iFrom = iFrom;
641 nmlv.iTo = iTo;
642 notify(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
645 static BOOL notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, HDC hdc, RECT rc)
647 NMLVCUSTOMDRAW nmlvcd;
649 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage, hdc);
651 nmlvcd.nmcd.dwDrawStage = dwDrawStage;
652 nmlvcd.nmcd.hdc = hdc;
653 nmlvcd.nmcd.rc = rc;
654 nmlvcd.nmcd.dwItemSpec = 0;
655 nmlvcd.nmcd.uItemState = 0;
656 nmlvcd.nmcd.lItemlParam = 0;
657 nmlvcd.clrText = infoPtr->clrText;
658 nmlvcd.clrTextBk = infoPtr->clrBk;
660 return (BOOL)notify(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
663 /* FIXME: we should inline this where it's called somehow
664 * I think we need to pass in the structure
666 static BOOL notify_customdrawitem (LISTVIEW_INFO *infoPtr, HDC hdc, UINT iItem, UINT iSubItem, UINT uItemDrawState)
668 NMLVCUSTOMDRAW nmlvcd;
669 UINT uItemState;
670 RECT itemRect;
671 LVITEMW item;
672 BOOL bReturn;
674 item.iItem = iItem;
675 item.iSubItem = 0;
676 item.mask = LVIF_PARAM;
677 if (!LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
679 uItemState = 0;
681 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_SELECTED)) uItemState |= CDIS_SELECTED;
682 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_FOCUSED)) uItemState |= CDIS_FOCUS;
683 if (iItem == infoPtr->nHotItem) uItemState |= CDIS_HOT;
685 itemRect.left = LVIR_BOUNDS;
686 LISTVIEW_GetItemRect(infoPtr, iItem, &itemRect);
688 nmlvcd.nmcd.dwDrawStage = CDDS_ITEM | uItemDrawState;
689 nmlvcd.nmcd.hdc = hdc;
690 nmlvcd.nmcd.rc = itemRect;
691 nmlvcd.nmcd.dwItemSpec = iItem;
692 nmlvcd.nmcd.uItemState = uItemState;
693 nmlvcd.nmcd.lItemlParam = item.lParam;
694 nmlvcd.clrText = infoPtr->clrText;
695 nmlvcd.clrTextBk = infoPtr->clrBk;
696 nmlvcd.iSubItem = iSubItem;
698 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
699 nmlvcd.nmcd.dwDrawStage, nmlvcd.nmcd.hdc, nmlvcd.nmcd.dwItemSpec,
700 nmlvcd.nmcd.uItemState, nmlvcd.nmcd.lItemlParam);
702 bReturn = notify(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
704 infoPtr->clrText = nmlvcd.clrText;
705 infoPtr->clrBk = nmlvcd.clrTextBk;
707 return bReturn;
710 /******** Misc helper functions ************************************/
712 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
713 WPARAM wParam, LPARAM lParam, BOOL isW)
715 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
716 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
719 /******** Internal API functions ************************************/
721 /* The Invalidate* are macros, so we preserve the caller location */
722 #define LISTVIEW_InvalidateRect(infoPtr, rect) do { \
723 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
724 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
725 } while (0)
727 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
728 RECT rcItem; \
729 rcItem.left = LVIR_BOUNDS; \
730 if(LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) \
731 LISTVIEW_InvalidateRect(infoPtr, &rcItem); \
732 } while (0)
734 #define LISTVIEW_InvalidateList(infoPtr)\
735 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcList)
737 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
739 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
742 static inline int LISTVIEW_GetType(LISTVIEW_INFO *infoPtr)
744 return infoPtr->dwStyle & LVS_TYPEMASK;
747 /***
748 * DESCRIPTION:
749 * Retrieves the number of items that can fit vertically in the client area.
751 * PARAMETER(S):
752 * [I] infoPtr : valid pointer to the listview structure
754 * RETURN:
755 * Number of items per row.
757 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
759 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
761 return max(nListWidth/infoPtr->nItemWidth, 1);
764 /***
765 * DESCRIPTION:
766 * Retrieves the number of items that can fit horizontally in the client
767 * area.
769 * PARAMETER(S):
770 * [I] infoPtr : valid pointer to the listview structure
772 * RETURN:
773 * Number of items per column.
775 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
777 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
779 return max(nListHeight / infoPtr->nItemHeight, 1);
782 /***
783 * DESCRIPTION:
784 * Retrieves the range of visible items. Note that the upper limit
785 * may be a bit larger than the actual last visible item.
787 * PARAMETER(S):
788 * [I] infoPtr : valid pointer to the listview structure
790 * RETURN:
791 * maximum range of visible items
793 static RANGE LISTVIEW_GetVisibleRange(LISTVIEW_INFO *infoPtr)
795 UINT uView = LISTVIEW_GetType(infoPtr);
796 INT nPerCol, nPerRow;
797 RANGE visrange;
799 visrange.lower = LISTVIEW_GetTopIndex(infoPtr);
801 if (uView == LVS_REPORT)
803 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
804 nPerRow = 1;
806 else if (uView == LVS_LIST)
808 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
809 nPerRow = LISTVIEW_GetCountPerRow(infoPtr);
811 else
813 /* FIXME: this is correct only in autoarrange mode */
814 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
815 nPerRow = LISTVIEW_GetCountPerRow(infoPtr) + 1;
818 visrange.upper = visrange.lower + nPerCol * nPerRow;
819 if (visrange.upper > infoPtr->nItemCount)
820 visrange.upper = infoPtr->nItemCount;
822 TRACE("range=(%d, %d)\n", visrange.lower, visrange.upper);
824 return visrange;
828 /*************************************************************************
829 * LISTVIEW_ProcessLetterKeys
831 * Processes keyboard messages generated by pressing the letter keys
832 * on the keyboard.
833 * What this does is perform a case insensitive search from the
834 * current position with the following quirks:
835 * - If two chars or more are pressed in quick succession we search
836 * for the corresponding string (e.g. 'abc').
837 * - If there is a delay we wipe away the current search string and
838 * restart with just that char.
839 * - If the user keeps pressing the same character, whether slowly or
840 * fast, so that the search string is entirely composed of this
841 * character ('aaaaa' for instance), then we search for first item
842 * that starting with that character.
843 * - If the user types the above character in quick succession, then
844 * we must also search for the corresponding string ('aaaaa'), and
845 * go to that string if there is a match.
847 * PARAMETERS
848 * [I] hwnd : handle to the window
849 * [I] charCode : the character code, the actual character
850 * [I] keyData : key data
852 * RETURNS
854 * Zero.
856 * BUGS
858 * - The current implementation has a list of characters it will
859 * accept and it ignores averything else. In particular it will
860 * ignore accentuated characters which seems to match what
861 * Windows does. But I'm not sure it makes sense to follow
862 * Windows there.
863 * - We don't sound a beep when the search fails.
865 * SEE ALSO
867 * TREEVIEW_ProcessLetterKeys
869 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
871 INT nItem;
872 INT endidx,idx;
873 LVITEMW item;
874 WCHAR buffer[MAX_PATH];
875 DWORD timestamp,elapsed;
877 /* simple parameter checking */
878 if (!charCode || !keyData) return 0;
880 /* only allow the valid WM_CHARs through */
881 if (!isalnum(charCode) &&
882 charCode != '.' && charCode != '`' && charCode != '!' &&
883 charCode != '@' && charCode != '#' && 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 return 0;
893 /* if there's one item or less, there is no where to go */
894 if (infoPtr->nItemCount <= 1) return 0;
896 /* compute how much time elapsed since last keypress */
897 timestamp=GetTickCount();
898 if (timestamp > infoPtr->lastKeyPressTimestamp) {
899 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
900 } else {
901 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
904 /* update the search parameters */
905 infoPtr->lastKeyPressTimestamp=timestamp;
906 if (elapsed < KEY_DELAY) {
907 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
908 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
910 if (infoPtr->charCode != charCode) {
911 infoPtr->charCode=charCode=0;
913 } else {
914 infoPtr->charCode=charCode;
915 infoPtr->szSearchParam[0]=charCode;
916 infoPtr->nSearchParamLength=1;
917 /* Redundant with the 1 char string */
918 charCode=0;
921 /* and search from the current position */
922 nItem=-1;
923 if (infoPtr->nFocusedItem >= 0) {
924 endidx=infoPtr->nFocusedItem;
925 idx=endidx;
926 /* if looking for single character match,
927 * then we must always move forward
929 if (infoPtr->nSearchParamLength == 1)
930 idx++;
931 } else {
932 endidx=infoPtr->nItemCount;
933 idx=0;
935 do {
936 if (idx == infoPtr->nItemCount) {
937 if (endidx == infoPtr->nItemCount || endidx == 0)
938 break;
939 idx=0;
942 /* get item */
943 item.mask = LVIF_TEXT;
944 item.iItem = idx;
945 item.iSubItem = 0;
946 item.pszText = buffer;
947 item.cchTextMax = COUNTOF(buffer);
948 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
950 /* check for a match */
951 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
952 nItem=idx;
953 break;
954 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
955 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
956 /* This would work but we must keep looking for a longer match */
957 nItem=idx;
959 idx++;
960 } while (idx != endidx);
962 if (nItem != -1)
963 LISTVIEW_KeySelection(infoPtr, nItem);
965 return 0;
968 /*************************************************************************
969 * LISTVIEW_UpdateHeaderSize [Internal]
971 * Function to resize the header control
973 * PARAMS
974 * hwnd [I] handle to a window
975 * nNewScrollPos [I] Scroll Pos to Set
977 * RETURNS
978 * nothing
980 * NOTES
982 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
984 RECT winRect;
985 POINT point[2];
987 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
989 GetWindowRect(infoPtr->hwndHeader, &winRect);
990 point[0].x = winRect.left;
991 point[0].y = winRect.top;
992 point[1].x = winRect.right;
993 point[1].y = winRect.bottom;
995 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
996 point[0].x = -nNewScrollPos;
997 point[1].x += nNewScrollPos;
999 SetWindowPos(infoPtr->hwndHeader,0,
1000 point[0].x,point[0].y,point[1].x,point[1].y,
1001 SWP_NOZORDER | SWP_NOACTIVATE);
1004 /***
1005 * DESCRIPTION:
1006 * Update the scrollbars. This functions should be called whenever
1007 * the content, size or view changes.
1009 * PARAMETER(S):
1010 * [I] infoPtr : valid pointer to the listview structure
1012 * RETURN:
1013 * None
1015 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1017 LONG lStyle = infoPtr->dwStyle;
1018 UINT uView = lStyle & LVS_TYPEMASK;
1019 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1020 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1021 SCROLLINFO scrollInfo;
1023 if (lStyle & LVS_NOSCROLL) return;
1025 scrollInfo.cbSize = sizeof(SCROLLINFO);
1027 if (uView == LVS_LIST)
1029 /* update horizontal scrollbar */
1030 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1031 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1033 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1034 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1036 scrollInfo.nMin = 0;
1037 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1038 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1039 scrollInfo.nMax--;
1040 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1041 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1042 scrollInfo.nPage = nCountPerRow;
1043 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1044 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1045 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1047 else if (uView == LVS_REPORT)
1049 BOOL test;
1051 /* update vertical scrollbar */
1052 scrollInfo.nMin = 0;
1053 scrollInfo.nMax = infoPtr->nItemCount - 1;
1054 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1055 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1056 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1057 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1058 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1059 scrollInfo.nMax, scrollInfo.nPage, test);
1060 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1061 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1063 /* update horizontal scrollbar */
1064 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1065 scrollInfo.fMask = SIF_POS;
1066 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1067 || infoPtr->nItemCount == 0)
1069 scrollInfo.nPos = 0;
1071 scrollInfo.nMin = 0;
1072 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1073 scrollInfo.nPage = nListWidth;
1074 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1075 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1076 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1077 scrollInfo.nMax, scrollInfo.nPage, test);
1078 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1079 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1081 /* Update the Header Control */
1082 scrollInfo.fMask = SIF_POS;
1083 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1084 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1087 else
1089 RECT rcView;
1091 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1093 INT nViewWidth = rcView.right - rcView.left;
1094 INT nViewHeight = rcView.bottom - rcView.top;
1096 /* Update Horizontal Scrollbar */
1097 scrollInfo.fMask = SIF_POS;
1098 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1099 || infoPtr->nItemCount == 0)
1101 scrollInfo.nPos = 0;
1103 scrollInfo.nMin = 0;
1104 scrollInfo.nMax = max(nViewWidth, 0)-1;
1105 scrollInfo.nPage = nListWidth;
1106 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1107 TRACE("LVS_ICON/SMALLICON Horz.\n");
1108 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1110 /* Update Vertical Scrollbar */
1111 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1112 scrollInfo.fMask = SIF_POS;
1113 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1114 || infoPtr->nItemCount == 0)
1116 scrollInfo.nPos = 0;
1118 scrollInfo.nMin = 0;
1119 scrollInfo.nMax = max(nViewHeight,0)-1;
1120 scrollInfo.nPage = nListHeight;
1121 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1122 TRACE("LVS_ICON/SMALLICON Vert.\n");
1123 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1129 /***
1130 * DESCRIPTION:
1131 * Shows/hides the focus rectangle.
1133 * PARAMETER(S):
1134 * [I] infoPtr : valid pointer to the listview structure
1135 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1137 * RETURN:
1138 * None
1140 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, INT nItem, BOOL fShow)
1142 TRACE("fShow=%d, nItem=%d\n", fShow, nItem);
1144 /* Here we are inneficient. We could, in theory, simply DrawFocusRect
1145 * to erase/show the focus, without all this heavy duty redraw. However,
1146 * note that there are cases where we can not do that: when the list is
1147 * in ICON mode, and the item is large, we must to invalidate it.
1148 * Moreover, in the vast majority of cases, the selection status of
1149 * the item changes anyway, and so the item is invalidated already,
1150 * so not too much harm is done. If we do notice any flicker, we should
1151 * refine this method. */
1152 LISTVIEW_InvalidateItem(infoPtr, nItem);
1155 /***
1156 * Invalidates all visible selected items.
1158 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1160 RANGE visrange;
1161 INT i;
1163 visrange = LISTVIEW_GetVisibleRange(infoPtr);
1164 for (i = visrange.lower; i <= visrange.upper; i++)
1166 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
1167 LISTVIEW_InvalidateItem(infoPtr, i);
1172 /***
1173 * DESCRIPTION:
1174 * Prints a message for unsupported window styles.
1175 * A kind of TODO list for window styles.
1177 * PARAMETER(S):
1178 * [I] LONG : window style
1180 * RETURN:
1181 * None
1183 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1185 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1186 FIXME(" LVS_NOSCROLL\n");
1188 if (lStyle & LVS_NOLABELWRAP)
1189 FIXME(" LVS_NOLABELWRAP\n");
1191 if (lStyle & LVS_SORTASCENDING)
1192 FIXME(" LVS_SORTASCENDING\n");
1194 if (lStyle & LVS_SORTDESCENDING)
1195 FIXME(" LVS_SORTDESCENDING\n");
1198 /***
1199 * DESCRIPTION: [INTERNAL]
1200 * Compute sizes and rectangles of an item. This is to localize all
1201 * the computations in one place. If you are not interested in some
1202 * of these values, simply pass in a NULL.
1204 * PARAMETER(S):
1205 * [I] infoPtr : valid pointer to the listview structure
1206 * [I] nItem : item number
1207 * [O] lpptPosition : ptr to Position point
1208 * Same as LVM_GETITEMPOSITION
1209 * [O] lprcBounds : ptr to Bounds rectangle
1210 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1211 * [O] lprcIcon : ptr to Icon rectangle
1212 * Same as LVM_GETITEMRECT with LVIR_ICON
1213 * [O] lprcLabel : ptr to Label rectangle
1214 * Same as LVM_GETITEMRECT with LVIR_LABEL
1216 * RETURN:
1217 * TRUE if computations OK
1218 * FALSE otherwise
1220 static BOOL LISTVIEW_GetItemMeasures(LISTVIEW_INFO *infoPtr, INT nItem,
1221 LPPOINT lpptPosition, LPRECT lprcBounds,
1222 LPRECT lprcIcon, LPRECT lprcLabel)
1224 LONG lStyle = infoPtr->dwStyle;
1225 UINT uView = lStyle & LVS_TYPEMASK;
1226 POINT Origin, Position, TopLeft;
1227 RECT Icon, Bounds, Label;
1228 INT nIndent = 0;
1230 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1232 if (uView == LVS_REPORT)
1234 LVITEMW lvItem;
1236 lvItem.mask = LVIF_INDENT;
1237 lvItem.iItem = nItem;
1238 lvItem.iSubItem = 0;
1239 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1241 /* do indent */
1242 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
1245 /************************************************************/
1246 /* compute the top, left corner of the absolute boundry box */
1247 /************************************************************/
1248 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1250 /* FIXME: what about virtual listview? */
1251 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
1252 LISTVIEW_ITEM * lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
1253 if (!lpItem) return FALSE;
1254 TopLeft = lpItem->ptPosition;
1256 else if (uView == LVS_LIST)
1258 INT nCountPerColumn;
1259 INT nRow, adjItem;
1261 adjItem = nItem - LISTVIEW_GetTopIndex(infoPtr);
1262 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1263 if (adjItem < 0)
1265 nRow = adjItem % nCountPerColumn;
1266 if (nRow == 0)
1268 TopLeft.x = adjItem / nCountPerColumn * infoPtr->nItemWidth;
1269 TopLeft.y = 0; /*FIXME: how so? */
1271 else
1273 TopLeft.x = (adjItem / nCountPerColumn -1) * infoPtr->nItemWidth;
1274 TopLeft.y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
1277 else
1279 TopLeft.x = adjItem / nCountPerColumn * infoPtr->nItemWidth;
1280 TopLeft.y = adjItem % nCountPerColumn * infoPtr->nItemHeight;
1283 else /* LVS_REPORT */
1285 TopLeft.x = REPORT_MARGINX;
1286 TopLeft.y = ((nItem - LISTVIEW_GetTopIndex(infoPtr)) *
1287 infoPtr->nItemHeight) + infoPtr->rcList.top;
1289 if (!(lStyle & LVS_NOSCROLL))
1290 { /*FIXME: why not use Origin? */
1291 SCROLLINFO scrollInfo;
1293 scrollInfo.cbSize = sizeof(SCROLLINFO);
1294 scrollInfo.fMask = SIF_POS;
1296 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
1297 TopLeft.x -= scrollInfo.nPos;
1301 /************************************************************/
1302 /* compute position point (ala LVM_GETITEMPOSITION) */
1303 /************************************************************/
1304 Position.x = TopLeft.x;
1305 Position.y = TopLeft.y;
1306 if (uView == LVS_ICON)
1308 Position.y += ICON_TOP_PADDING;
1309 Position.x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1311 if (lpptPosition) *lpptPosition = Position;
1312 TRACE("hwnd=%x, item=%d, position=(%ld,%ld)\n",
1313 infoPtr->hwndSelf, nItem, Position.x, Position.y);
1315 /************************************************************/
1316 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1317 /************************************************************/
1318 if (uView == LVS_ICON)
1320 if (infoPtr->himlNormal != NULL)
1322 Icon.left = Position.x + Origin.x - ICON_LR_HALF;
1323 Icon.top = Position.y + Origin.y - ICON_TOP_PADDING;
1324 Icon.right = Icon.left + infoPtr->iconSize.cx + ICON_LR_PADDING;
1325 Icon.bottom = Icon.top + infoPtr->iconSize.cy + ICON_TOP_PADDING;
1327 else return FALSE;
1329 else if (uView == LVS_SMALLICON)
1331 Icon.left = Position.x + Origin.x;
1332 Icon.top = Position.y + Origin.y;
1333 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1335 if (infoPtr->himlState != NULL)
1336 Icon.left += infoPtr->iconStateSize.cx;
1338 Icon.right = Icon.left;
1339 if (infoPtr->himlSmall != NULL)
1340 Icon.right += infoPtr->iconSize.cx;
1342 else /* LVS_LIST or LVS_REPORT */
1344 /* FIXME: why is the one above relative to origin??? */
1345 Icon.left = Position.x;
1346 Icon.top = Position.y;
1347 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1349 if (uView & LVS_REPORT)
1350 Icon.left += nIndent;
1352 if (infoPtr->himlState != NULL)
1353 Icon.left += infoPtr->iconStateSize.cx;
1355 Icon.right = Icon.left;
1356 if (infoPtr->himlSmall != NULL)
1357 Icon.right += infoPtr->iconSize.cx;
1360 if(lprcIcon) *lprcIcon = Icon;
1361 TRACE("hwnd=%x, item=%d, icon=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Icon));
1363 /************************************************************/
1364 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1365 /************************************************************/
1366 if (uView == LVS_ICON)
1368 if (infoPtr->himlNormal != NULL)
1370 INT nLabelWidth;
1372 Label.left = TopLeft.x + Origin.x;
1373 Label.top = TopLeft.y + Origin.y + ICON_TOP_PADDING_HITABLE +
1374 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1376 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1377 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
1379 Label.left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
1380 Label.right = Label.left + nLabelWidth;
1381 Label.bottom = Label.top + infoPtr->ntmHeight + 1;
1382 Label.bottom += HEIGHT_PADDING;
1384 else
1386 Label.right = Label.left + infoPtr->nItemWidth;
1387 Label.bottom = Label.top + infoPtr->nItemHeight + HEIGHT_PADDING;
1388 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, &Label);
1390 if (lprcLabel) *lprcLabel = Label;
1392 else return FALSE;
1394 else if (uView == LVS_SMALLICON)
1396 INT nLeftPos, nLabelWidth;
1398 nLeftPos = Label.left = Position.x + Origin.x;
1399 Label.top = Position.y + Origin.y;
1400 Label.bottom = Label.top + infoPtr->nItemHeight;
1402 if (infoPtr->himlState != NULL)
1403 Label.left += infoPtr->iconStateSize.cx;
1405 if (infoPtr->himlSmall != NULL)
1406 Label.left += infoPtr->iconSize.cx;
1408 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1409 nLabelWidth += TRAILING_PADDING;
1410 if (Label.left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
1411 Label.right = Label.left + nLabelWidth;
1412 else
1413 Label.right = nLeftPos + infoPtr->nItemWidth;
1414 if (lprcLabel) *lprcLabel = Label;
1416 else /* LVS_LIST or LVS_REPORT */
1418 INT nLabelWidth;
1420 if (uView != LVS_REPORT)
1422 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1423 nLabelWidth += TRAILING_PADDING;
1424 if (infoPtr->himlSmall) nLabelWidth += IMAGE_PADDING;
1426 else
1427 nLabelWidth = LISTVIEW_GetColumnWidth(infoPtr, 0) - Icon.left;
1429 Label.left = Icon.right;
1430 Label.top = Position.y;
1431 Label.right = Label.left + nLabelWidth;
1432 Label.bottom = Label.top + infoPtr->nItemHeight;
1434 if (Label.right - Position.x > infoPtr->nItemWidth)
1435 Label.right = Position.x + infoPtr->nItemWidth;
1436 if (lprcLabel) *lprcLabel = Label;
1439 TRACE("hwnd=%x, item=%d, label=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Label));
1441 /***********************************************************/
1442 /* compute bounds box for the item (ala LVM_GETITEMRECT) */
1443 /***********************************************************/
1445 if (uView == LVS_REPORT)
1447 Bounds.left = TopLeft.x;
1448 Bounds.top = TopLeft.y;
1449 Bounds.right = Bounds.left + infoPtr->nItemWidth;
1450 Bounds.bottom = Bounds.top + infoPtr->nItemHeight;
1451 if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
1452 Bounds.left += nIndent;
1453 Bounds.right -= REPORT_MARGINX;
1454 if (Bounds.right < Bounds.left) Bounds.right = Bounds.left;
1456 else
1458 UnionRect(&Bounds, &Icon, &Label);
1461 TRACE("hwnd=%x, item=%d, bounds=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Bounds));
1462 if (lprcBounds) *lprcBounds = Bounds;
1464 return TRUE;
1467 /***
1468 * DESCRIPTION:
1469 * Aligns the items with the top edge of the window.
1471 * PARAMETER(S):
1472 * [I] infoPtr : valid pointer to the listview structure
1474 * RETURN:
1475 * None
1477 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1479 UINT uView = LISTVIEW_GetType(infoPtr);
1480 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1481 POINT ptItem;
1482 RECT rcView;
1483 INT i, off_x=0, off_y=0;
1485 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1487 /* Since SetItemPosition uses upper-left of icon, and for
1488 style=LVS_ICON the icon is not left adjusted, get the offset */
1489 if (uView == LVS_ICON)
1491 off_y = ICON_TOP_PADDING;
1492 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1494 ptItem.x = off_x;
1495 ptItem.y = off_y;
1496 ZeroMemory(&rcView, sizeof(RECT));
1497 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1498 off_x, off_y,
1499 infoPtr->rcList.left, infoPtr->rcList.right);
1501 if (nListWidth > infoPtr->nItemWidth)
1503 for (i = 0; i < infoPtr->nItemCount; i++)
1505 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1507 ptItem.x = off_x;
1508 ptItem.y += infoPtr->nItemHeight;
1511 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1512 ptItem.x += infoPtr->nItemWidth;
1513 rcView.right = max(rcView.right, ptItem.x);
1516 rcView.right -= off_x;
1517 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1519 else
1521 for (i = 0; i < infoPtr->nItemCount; i++)
1523 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1524 ptItem.y += infoPtr->nItemHeight;
1527 rcView.right = infoPtr->nItemWidth;
1528 rcView.bottom = ptItem.y-off_y;
1531 infoPtr->rcView = rcView;
1535 /***
1536 * DESCRIPTION:
1537 * Aligns the items with the left edge of the window.
1539 * PARAMETER(S):
1540 * [I] infoPtr : valid pointer to the listview structure
1542 * RETURN:
1543 * None
1545 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1547 UINT uView = LISTVIEW_GetType(infoPtr);
1548 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1549 POINT ptItem;
1550 RECT rcView;
1551 INT i, off_x=0, off_y=0;
1553 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1555 /* Since SetItemPosition uses upper-left of icon, and for
1556 style=LVS_ICON the icon is not left adjusted, get the offset */
1557 if (uView == LVS_ICON)
1559 off_y = ICON_TOP_PADDING;
1560 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1562 ptItem.x = off_x;
1563 ptItem.y = off_y;
1564 ZeroMemory(&rcView, sizeof(RECT));
1565 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1567 if (nListHeight > infoPtr->nItemHeight)
1569 for (i = 0; i < infoPtr->nItemCount; i++)
1571 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1573 ptItem.y = off_y;
1574 ptItem.x += infoPtr->nItemWidth;
1577 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1578 ptItem.y += infoPtr->nItemHeight;
1579 rcView.bottom = max(rcView.bottom, ptItem.y);
1582 rcView.right = ptItem.x + infoPtr->nItemWidth;
1584 else
1586 for (i = 0; i < infoPtr->nItemCount; i++)
1588 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1589 ptItem.x += infoPtr->nItemWidth;
1592 rcView.bottom = infoPtr->nItemHeight;
1593 rcView.right = ptItem.x;
1596 infoPtr->rcView = rcView;
1601 /***
1602 * DESCRIPTION:
1603 * Retrieves the bounding rectangle of all the items.
1605 * PARAMETER(S):
1606 * [I] infoPtr : valid pointer to the listview structure
1607 * [O] lprcView : bounding rectangle
1609 * RETURN:
1610 * SUCCESS : TRUE
1611 * FAILURE : FALSE
1613 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1615 POINT ptOrigin;
1617 TRACE("(lprcView=%p)\n", lprcView);
1619 if (!lprcView) return FALSE;
1621 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1623 *lprcView = infoPtr->rcView;
1624 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1626 TRACE("lprcView=%s\n", debugrect(lprcView));
1628 return TRUE;
1631 /***
1632 * DESCRIPTION:
1633 * Retrieves the subitem pointer associated with the subitem index.
1635 * PARAMETER(S):
1636 * [I] HDPA : DPA handle for a specific item
1637 * [I] INT : index of subitem
1639 * RETURN:
1640 * SUCCESS : subitem pointer
1641 * FAILURE : NULL
1643 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1644 INT nSubItem)
1646 LISTVIEW_SUBITEM *lpSubItem;
1647 INT i;
1649 /* we should binary search here if need be */
1650 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1652 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1653 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1654 return lpSubItem;
1657 return NULL;
1661 /***
1662 * DESCRIPTION:
1663 * Calculates the width of a specific item.
1665 * PARAMETER(S):
1666 * [I] infoPtr : valid pointer to the listview structure
1667 * [I] nItem : item to calculate width, or -1 for max of all
1669 * RETURN:
1670 * Returns the width of an item width an item.
1672 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1674 UINT uView = LISTVIEW_GetType(infoPtr);
1675 INT nItemWidth = 0, i;
1677 if (uView == LVS_ICON)
1678 nItemWidth = infoPtr->iconSpacing.cx;
1679 else if (uView == LVS_REPORT)
1681 INT nHeaderItemCount;
1682 RECT rcHeaderItem;
1684 /* calculate width of header */
1685 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1686 for (i = 0; i < nHeaderItemCount; i++)
1687 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
1688 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1690 else
1692 INT nLabelWidth;
1694 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
1696 /* get width of string */
1697 if (nItem == -1)
1699 for (i = 0; i < infoPtr->nItemCount; i++)
1701 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1702 nItemWidth = max(nItemWidth, nLabelWidth);
1705 else
1706 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1707 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
1708 nItemWidth += WIDTH_PADDING;
1709 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
1710 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
1711 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1714 return max(nItemWidth, 1);
1717 /***
1718 * DESCRIPTION:
1719 * Calculates the max width of any item in the list.
1721 * PARAMETER(S):
1722 * [I] infoPtr : valid pointer to the listview structure
1723 * [I] LONG : window style
1725 * RETURN:
1726 * Returns item width.
1728 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
1730 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
1733 /***
1734 * DESCRIPTION:
1735 * Retrieves and saves important text metrics info for the current
1736 * Listview font.
1738 * PARAMETER(S):
1739 * [I] infoPtr : valid pointer to the listview structure
1742 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1744 TEXTMETRICW tm;
1745 HDC hdc = GetDC(infoPtr->hwndSelf);
1746 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1747 INT oldHeight, oldACW;
1749 GetTextMetricsW(hdc, &tm);
1751 oldHeight = infoPtr->ntmHeight;
1752 oldACW = infoPtr->ntmAveCharWidth;
1753 infoPtr->ntmHeight = tm.tmHeight;
1754 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1756 SelectObject(hdc, hOldFont);
1757 ReleaseDC(infoPtr->hwndSelf, hdc);
1758 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1759 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1763 /***
1764 * DESCRIPTION:
1765 * Calculates the height of an item.
1767 * PARAMETER(S):
1768 * [I] infoPtr : valid pointer to the listview structure
1770 * RETURN:
1771 * Returns item height.
1773 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1775 INT nItemHeight;
1777 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
1778 nItemHeight = infoPtr->iconSpacing.cy;
1779 else
1781 nItemHeight = infoPtr->ntmHeight;
1782 if (infoPtr->himlState)
1783 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
1784 if (infoPtr->himlSmall)
1785 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
1786 if (infoPtr->himlState || infoPtr->himlSmall)
1787 nItemHeight += HEIGHT_PADDING;
1789 return nItemHeight;
1792 #if 0
1793 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1795 INT i;
1797 ERR("Selections are:\n");
1798 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
1800 RANGE *selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
1801 ERR(" [%d - %d]\n", selection->lower, selection->upper);
1804 #endif
1806 /***
1807 * DESCRIPTION:
1808 * A compare function for selection ranges
1810 *PARAMETER(S)
1811 * [I] range1 : pointer to selection range 1;
1812 * [I] range2 : pointer to selection range 2;
1813 * [I] flags : flags
1815 *RETURNS:
1816 * >0 : if Item 1 > Item 2
1817 * <0 : if Item 2 > Item 1
1818 * 0 : if Item 1 == Item 2
1820 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2, LPARAM flags)
1822 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
1823 return -1;
1824 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
1825 return 1;
1826 return 0;
1829 /***
1830 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
1832 static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1834 RANGE selection;
1835 LVITEMW lvItem;
1836 INT index, i;
1838 TRACE("range (%i - %i)\n", lower, upper);
1840 /* try find overlapping selections first */
1841 selection.lower = lower - 1;
1842 selection.upper = upper + 1;
1843 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1844 LISTVIEW_CompareSelectionRanges, 0, 0);
1846 if (index == -1)
1848 RANGE *newsel;
1850 /* create the brand new selection to insert */
1851 newsel = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
1852 if(!newsel) return FALSE;
1853 newsel->lower = lower;
1854 newsel->upper = upper;
1856 /* figure out where to insert it */
1857 index = DPA_Search(infoPtr->hdpaSelectionRanges, newsel, 0,
1858 LISTVIEW_CompareSelectionRanges, 0, DPAS_INSERTAFTER);
1859 if (index == -1) index = 0;
1861 /* and get it over with */
1862 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
1864 else
1866 RANGE *chksel, *mrgsel;
1867 INT fromindex, mergeindex;
1869 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1870 if (!chksel) return FALSE;
1871 TRACE("Merge with index %i (%d - %d)\n",
1872 index, chksel->lower, chksel->upper);
1874 chksel->lower = min(lower, chksel->lower);
1875 chksel->upper = max(upper, chksel->upper);
1877 TRACE("New range %i (%d - %d)\n",
1878 index, chksel->lower, chksel->upper);
1880 /* merge now common selection ranges */
1881 fromindex = 0;
1882 selection.lower = chksel->lower - 1;
1883 selection.upper = chksel->upper + 1;
1887 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, fromindex,
1888 LISTVIEW_CompareSelectionRanges, 0, 0);
1889 if (mergeindex == -1) break;
1890 if (mergeindex == index)
1892 fromindex = index + 1;
1893 continue;
1896 TRACE("Merge with index %i\n", mergeindex);
1898 mrgsel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, mergeindex);
1899 if (!mrgsel) return FALSE;
1901 chksel->lower = min(chksel->lower, mrgsel->lower);
1902 chksel->upper = max(chksel->upper, mrgsel->upper);
1903 COMCTL32_Free(mrgsel);
1904 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, mergeindex);
1905 if (mergeindex < index) index --;
1906 } while(1);
1909 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
1911 if (adj_sel_only) return TRUE;
1913 /* set the selection on items */
1914 lvItem.state = LVIS_SELECTED;
1915 lvItem.stateMask = LVIS_SELECTED;
1916 for(i = lower; i <= upper; i++)
1917 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
1919 return TRUE;
1922 /***
1923 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
1925 static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1927 RANGE remsel, tmpsel, *chksel;
1928 BOOL done = FALSE;
1929 LVITEMW lvItem;
1930 INT index, i;
1932 lvItem.state = 0;
1933 lvItem.stateMask = LVIS_SELECTED;
1935 remsel.lower = lower;
1936 remsel.upper = upper;
1938 TRACE("range: (%d - %d)\n", remsel.lower, remsel.upper);
1942 index = DPA_Search(infoPtr->hdpaSelectionRanges, &remsel, 0,
1943 LISTVIEW_CompareSelectionRanges, 0, 0);
1944 if (index == -1) return TRUE;
1946 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1947 if (!chksel) return FALSE;
1949 TRACE("Matches range index %i (%d - %d)\n",
1950 index, chksel->lower, chksel->upper);
1952 /* case 1: Same range */
1953 if ( (chksel->upper == remsel.upper) &&
1954 (chksel->lower == remsel.lower) )
1956 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1957 done = TRUE;
1959 /* case 2: engulf */
1960 else if ( (chksel->upper <= remsel.upper) &&
1961 (chksel->lower >= remsel.lower) )
1963 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1965 /* case 3: overlap upper */
1966 else if ( (chksel->upper < remsel.upper) &&
1967 (chksel->lower < remsel.lower) )
1969 chksel->upper = remsel.lower - 1;
1971 /* case 4: overlap lower */
1972 else if ( (chksel->upper > remsel.upper) &&
1973 (chksel->lower > remsel.lower) )
1975 chksel->lower = remsel.upper + 1;
1977 /* case 5: fully internal */
1978 else
1980 RANGE *newsel =
1981 (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
1982 if (!newsel) return FALSE;
1983 tmpsel = *chksel;
1984 newsel->lower = chksel->lower;
1985 newsel->upper = remsel.lower - 1;
1986 chksel->lower = remsel.upper + 1;
1987 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
1988 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
1989 chksel = &tmpsel;
1992 if (adj_sel_only) continue;
1994 /* here, chksel holds the selection to delete */
1995 for (i = chksel->lower; i <= chksel->upper; i++)
1996 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
1998 while(!done);
2000 return TRUE;
2004 * DESCRIPTION:
2005 * Adds a selection range.
2007 * PARAMETER(S):
2008 * [I] infoPtr : valid pointer to the listview structure
2009 * [I] lower : lower item index
2010 * [I] upper : upper item index
2012 * RETURN:
2013 * Success: TRUE
2014 * Failure: FALSE
2016 static inline BOOL LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2018 return add_selection_range(infoPtr, lower, upper, FALSE);
2021 /***
2022 * DESCRIPTION:
2023 * Removes a range selections.
2025 * PARAMETER(S):
2026 * [I] infoPtr : valid pointer to the listview structure
2027 * [I] lower : lower item index
2028 * [I] upper : upper item index
2030 * RETURN:
2031 * Success: TRUE
2032 * Failure: FALSE
2034 static inline BOOL LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2036 return remove_selection_range(infoPtr, lower, upper, FALSE);
2039 /***
2040 * DESCRIPTION:
2041 * Removes all selection ranges
2043 * Parameters(s):
2044 * [I] infoPtr : valid pointer to the listview structure
2046 * RETURNS:
2047 * SUCCESS : TRUE
2048 * FAILURE : TRUE
2050 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
2052 RANGE *sel;
2054 if (infoPtr->bRemovingAllSelections) return TRUE;
2056 infoPtr->bRemovingAllSelections = TRUE;
2058 TRACE("()\n");
2062 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, 0);
2063 if (sel) LISTVIEW_RemoveSelectionRange(infoPtr, sel->lower, sel->upper);
2065 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
2067 infoPtr->bRemovingAllSelections = FALSE;
2069 return TRUE;
2072 /***
2073 * DESCRIPTION:
2074 * Retrieves the number of items that are marked as selected.
2076 * PARAMETER(S):
2077 * [I] infoPtr : valid pointer to the listview structure
2079 * RETURN:
2080 * Number of items selected.
2082 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2084 INT i, nSelectedCount = 0;
2086 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2088 INT i;
2089 for (i = 0; i < infoPtr->nItemCount; i++)
2091 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2092 nSelectedCount++;
2095 else
2097 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2099 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2100 nSelectedCount += sel->upper - sel->lower + 1;
2104 TRACE("nSelectedCount=%d\n", nSelectedCount);
2105 return nSelectedCount;
2108 /***
2109 * DESCRIPTION:
2110 * Manages the item focus.
2112 * PARAMETER(S):
2113 * [I] infoPtr : valid pointer to the listview structure
2114 * [I] INT : item index
2116 * RETURN:
2117 * TRUE : focused item changed
2118 * FALSE : focused item has NOT changed
2120 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2122 INT oldFocus = infoPtr->nFocusedItem;
2123 LVITEMW lvItem;
2125 lvItem.state = LVIS_FOCUSED;
2126 lvItem.stateMask = LVIS_FOCUSED;
2127 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2129 return oldFocus != infoPtr->nFocusedItem;
2133 * DESCRIPTION:
2134 * Updates the various indices after an item has been inserted or deleted.
2136 * PARAMETER(S):
2137 * [I] infoPtr : valid pointer to the listview structure
2138 * [I] nItem : item index
2139 * [I] direction : Direction of shift, +1 or -1.
2141 * RETURN:
2142 * None
2144 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2146 RANGE selection,*checkselection;
2147 INT index;
2149 TRACE("Shifting %iu, %i steps\n",nItem,direction);
2151 selection.upper = nItem;
2152 selection.lower = nItem;
2154 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
2155 LISTVIEW_CompareSelectionRanges,
2156 0,DPAS_SORTED|DPAS_INSERTAFTER);
2158 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
2160 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
2161 if ((checkselection->lower >= nItem)&&
2162 ((int)(checkselection->lower + direction) >= 0))
2163 checkselection->lower += direction;
2164 if ((checkselection->upper >= nItem)&&
2165 ((int)(checkselection->upper + direction) >= 0))
2166 checkselection->upper += direction;
2167 index ++;
2170 /* Note that the following will fail if direction != +1 and -1 */
2171 if (infoPtr->nSelectionMark > nItem)
2172 infoPtr->nSelectionMark += direction;
2173 else if (infoPtr->nSelectionMark == nItem)
2175 if (direction > 0)
2176 infoPtr->nSelectionMark += direction;
2177 else if (infoPtr->nSelectionMark >= infoPtr->nItemCount)
2178 infoPtr->nSelectionMark = infoPtr->nItemCount - 1;
2181 if (infoPtr->nFocusedItem > nItem)
2182 infoPtr->nFocusedItem += direction;
2183 else if (infoPtr->nFocusedItem == nItem)
2185 if (direction > 0)
2186 infoPtr->nFocusedItem += direction;
2187 else
2189 if (infoPtr->nFocusedItem >= infoPtr->nItemCount)
2190 infoPtr->nFocusedItem = infoPtr->nItemCount - 1;
2191 if (infoPtr->nFocusedItem >= 0)
2192 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
2195 /* But we are not supposed to modify nHotItem! */
2200 * DESCRIPTION:
2201 * Adds a block of selections.
2203 * PARAMETER(S):
2204 * [I] infoPtr : valid pointer to the listview structure
2205 * [I] INT : item index
2207 * RETURN:
2208 * None
2210 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2212 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2213 INT nLast = max(infoPtr->nSelectionMark, nItem);
2214 INT i;
2215 LVITEMW item;
2217 if (nFirst == -1)
2218 nFirst = nItem;
2220 item.state = LVIS_SELECTED;
2221 item.stateMask = LVIS_SELECTED;
2223 /* FIXME: this is not correct LVS_OWNERDATA
2224 * See docu for LVN_ITEMCHANGED. Is there something similar for
2225 * RemoveGroupSelection (is there such a thing?)?
2227 for (i = nFirst; i <= nLast; i++)
2228 LISTVIEW_SetItemState(infoPtr,i,&item);
2230 LISTVIEW_SetItemFocus(infoPtr, nItem);
2231 infoPtr->nSelectionMark = nItem;
2235 /***
2236 * DESCRIPTION:
2237 * Sets a single group selection.
2239 * PARAMETER(S):
2240 * [I] infoPtr : valid pointer to the listview structure
2241 * [I] INT : item index
2243 * RETURN:
2244 * None
2246 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2248 UINT uView = LISTVIEW_GetType(infoPtr);
2249 INT i;
2250 LVITEMW item;
2251 POINT ptItem;
2252 RECT rcSel;
2254 LISTVIEW_RemoveAllSelections(infoPtr);
2256 item.state = LVIS_SELECTED;
2257 item.stateMask = LVIS_SELECTED;
2259 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2261 INT nFirst, nLast;
2263 if (infoPtr->nSelectionMark == -1)
2264 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2265 else
2267 nFirst = min(infoPtr->nSelectionMark, nItem);
2268 nLast = max(infoPtr->nSelectionMark, nItem);
2270 for (i = nFirst; i <= nLast; i++)
2271 LISTVIEW_SetItemState(infoPtr, i, &item);
2273 else
2275 RECT rcItem, rcSelMark;
2277 rcItem.left = LVIR_BOUNDS;
2278 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2279 rcSelMark.left = LVIR_BOUNDS;
2280 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2281 UnionRect(&rcSel, &rcItem, &rcSelMark);
2282 for (i = 0; i <= infoPtr->nItemCount; i++)
2284 LISTVIEW_GetItemPosition(infoPtr, i, &ptItem);
2285 if (PtInRect(&rcSel, ptItem))
2286 LISTVIEW_SetItemState(infoPtr, i, &item);
2290 LISTVIEW_SetItemFocus(infoPtr, nItem);
2293 /***
2294 * DESCRIPTION:
2295 * Sets a single selection.
2297 * PARAMETER(S):
2298 * [I] infoPtr : valid pointer to the listview structure
2299 * [I] INT : item index
2301 * RETURN:
2302 * None
2304 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2306 LVITEMW lvItem;
2308 TRACE("nItem=%d\n", nItem);
2310 LISTVIEW_RemoveAllSelections(infoPtr);
2312 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2313 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2314 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2316 infoPtr->nSelectionMark = nItem;
2319 /***
2320 * DESCRIPTION:
2321 * Set selection(s) with keyboard.
2323 * PARAMETER(S):
2324 * [I] infoPtr : valid pointer to the listview structure
2325 * [I] INT : item index
2327 * RETURN:
2328 * SUCCESS : TRUE (needs to be repainted)
2329 * FAILURE : FALSE (nothing has changed)
2331 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2333 /* FIXME: pass in the state */
2334 LONG lStyle = infoPtr->dwStyle;
2335 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2336 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2337 BOOL bResult = FALSE;
2339 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2341 if (lStyle & LVS_SINGLESEL)
2343 bResult = TRUE;
2344 LISTVIEW_SetSelection(infoPtr, nItem);
2345 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2347 else
2349 if (wShift)
2351 bResult = TRUE;
2352 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2354 else if (wCtrl)
2356 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2358 else
2360 bResult = TRUE;
2361 LISTVIEW_SetSelection(infoPtr, nItem);
2362 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2367 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2368 return bResult;
2371 /***
2372 * DESCRIPTION:
2373 * Selects an item based on coordinates.
2375 * PARAMETER(S):
2376 * [I] infoPtr : valid pointer to the listview structure
2377 * [I] pt : mouse click ccordinates
2379 * RETURN:
2380 * SUCCESS : item index
2381 * FAILURE : -1
2383 static INT LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, POINT pt)
2385 RANGE visrange;
2386 RECT rcItem;
2387 INT i;
2389 visrange = LISTVIEW_GetVisibleRange(infoPtr);
2390 for (i = visrange.lower; i <= visrange.upper; i++)
2392 rcItem.left = LVIR_SELECTBOUNDS;
2393 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
2395 TRACE("i=%d, rcItem=%s\n", i, debugrect(&rcItem));
2396 if (PtInRect(&rcItem, pt)) return i;
2399 return -1;
2402 /***
2403 * DESCRIPTION:
2404 * Called when the mouse is being actively tracked and has hovered for a specified
2405 * amount of time
2407 * PARAMETER(S):
2408 * [I] infoPtr : valid pointer to the listview structure
2409 * [I] fwKeys : key indicator
2410 * [I] pts : mouse position
2412 * RETURN:
2413 * 0 if the message was processed, non-zero if there was an error
2415 * INFO:
2416 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2417 * over the item for a certain period of time.
2420 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2422 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2423 /* FIXME: select the item!!! */
2424 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2426 return 0;
2429 /***
2430 * DESCRIPTION:
2431 * Called whenever WM_MOUSEMOVE is received.
2433 * PARAMETER(S):
2434 * [I] infoPtr : valid pointer to the listview structure
2435 * [I] fwKeys : key indicator
2436 * [I] pts : mouse position
2438 * RETURN:
2439 * 0 if the message is processed, non-zero if there was an error
2441 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2443 TRACKMOUSEEVENT trackinfo;
2445 /* see if we are supposed to be tracking mouse hovering */
2446 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2447 /* fill in the trackinfo struct */
2448 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2449 trackinfo.dwFlags = TME_QUERY;
2450 trackinfo.hwndTrack = infoPtr->hwndSelf;
2451 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2453 /* see if we are already tracking this hwnd */
2454 _TrackMouseEvent(&trackinfo);
2456 if(!(trackinfo.dwFlags & TME_HOVER)) {
2457 trackinfo.dwFlags = TME_HOVER;
2459 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2460 _TrackMouseEvent(&trackinfo);
2464 return 0;
2468 /***
2469 * Tests wheather the item is assignable to a list with style lStyle
2471 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2473 if ( (lpLVItem->mask & LVIF_TEXT) &&
2474 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2475 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2477 return TRUE;
2480 /***
2481 * DESCRIPTION:
2482 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2484 * PARAMETER(S):
2485 * [I] infoPtr : valid pointer to the listview structure
2486 * [I] lpLVItem : valid pointer to new item atttributes
2487 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2489 * RETURN:
2490 * SUCCESS : TRUE
2491 * FAILURE : FALSE
2493 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2495 LONG lStyle = infoPtr->dwStyle;
2496 NMLISTVIEW nmlv;
2497 INT oldState;
2499 /* a virtual listview stores only the state for the main item */
2500 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2502 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2503 TRACE("oldState=%x, newState=%x, uCallbackMask=%x\n",
2504 oldState, lpLVItem->state, infoPtr->uCallbackMask);
2506 /* we're done if we don't need to change anything we handle */
2507 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2508 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2511 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2512 * by LVS_OWERNDATA list controls
2515 /* if we handle the focus, and we're asked to change it, do it now */
2516 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2518 if (lpLVItem->state & LVIS_FOCUSED)
2519 infoPtr->nFocusedItem = lpLVItem->iItem;
2520 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2521 infoPtr->nFocusedItem = -1;
2524 /* and the selection is the only other state a virtual list may hold */
2525 if (lpLVItem->stateMask & LVIS_SELECTED)
2527 if (lpLVItem->state & LVIS_SELECTED)
2529 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2530 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2532 else
2533 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2536 /* notify the parent now that things have changed */
2537 ZeroMemory(&nmlv, sizeof(nmlv));
2538 nmlv.iItem = lpLVItem->iItem;
2539 nmlv.uNewState = lpLVItem->state;
2540 nmlv.uOldState = oldState;
2541 nmlv.uChanged = LVIF_STATE;
2542 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2544 return TRUE;
2547 /***
2548 * DESCRIPTION:
2549 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2551 * PARAMETER(S):
2552 * [I] infoPtr : valid pointer to the listview structure
2553 * [I] lpLVItem : valid pointer to new item atttributes
2554 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2556 * RETURN:
2557 * SUCCESS : TRUE
2558 * FAILURE : FALSE
2560 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2562 LONG lStyle = infoPtr->dwStyle;
2563 UINT uView = lStyle & LVS_TYPEMASK;
2564 HDPA hdpaSubItems;
2565 LISTVIEW_ITEM *lpItem;
2566 NMLISTVIEW nmlv;
2567 UINT uChanged = 0;
2569 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2570 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2572 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2573 if (!lpItem) return FALSE;
2575 /* determine what fields will change */
2576 if ((lpLVItem->mask & LVIF_STATE) &&
2577 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask))
2578 uChanged |= LVIF_STATE;
2580 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2581 uChanged |= LVIF_IMAGE;
2583 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2584 uChanged |= LVIF_PARAM;
2586 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2587 uChanged |= LVIF_INDENT;
2589 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2590 uChanged |= LVIF_TEXT;
2592 if (!uChanged) return TRUE;
2594 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2595 nmlv.iItem = lpLVItem->iItem;
2596 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2597 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2598 nmlv.uChanged = uChanged;
2599 nmlv.lParam = lpItem->lParam;
2601 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2602 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2603 return FALSE;
2605 /* copy information */
2606 if (lpLVItem->mask & LVIF_TEXT)
2607 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2609 if (lpLVItem->mask & LVIF_IMAGE)
2610 lpItem->hdr.iImage = lpLVItem->iImage;
2612 if (lpLVItem->mask & LVIF_PARAM)
2613 lpItem->lParam = lpLVItem->lParam;
2615 if (lpLVItem->mask & LVIF_INDENT)
2616 lpItem->iIndent = lpLVItem->iIndent;
2618 if (uChanged & LVIF_STATE)
2620 lpItem->state &= ~lpLVItem->stateMask;
2621 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2622 if (nmlv.uNewState & LVIS_SELECTED)
2624 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2625 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2627 else if (lpLVItem->stateMask & LVIS_SELECTED)
2628 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2630 /* if we are asked to change focus, and we manage it, do it */
2631 if (nmlv.uNewState & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2633 if (lpLVItem->state & LVIS_FOCUSED)
2635 infoPtr->nFocusedItem = lpLVItem->iItem;
2636 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2638 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2639 infoPtr->nFocusedItem = -1;
2643 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2644 if((uChanged & LVIF_TEXT) && ((uView == LVS_LIST) || (uView == LVS_SMALLICON)))
2646 int item_width = LISTVIEW_CalculateItemWidth(infoPtr, lpLVItem->iItem);
2647 if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width;
2650 /* if we're inserting the item, we're done */
2651 if (!lpItem->valid) return TRUE;
2653 /* send LVN_ITEMCHANGED notification */
2654 nmlv.lParam = lpItem->lParam;
2655 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2657 return TRUE;
2660 /***
2661 * DESCRIPTION:
2662 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2664 * PARAMETER(S):
2665 * [I] infoPtr : valid pointer to the listview structure
2666 * [I] lpLVItem : valid pointer to new subitem atttributes
2667 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2669 * RETURN:
2670 * SUCCESS : TRUE
2671 * FAILURE : FALSE
2673 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2675 HDPA hdpaSubItems;
2676 LISTVIEW_SUBITEM *lpSubItem;
2677 BOOL bModified = FALSE;
2679 /* set subitem only if column is present */
2680 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2681 return FALSE;
2683 /* First do some sanity checks */
2684 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2685 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
2687 /* get the subitem structure, and create it if not there */
2688 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2689 if (!hdpaSubItems) return FALSE;
2691 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2692 if (!lpSubItem)
2694 LISTVIEW_SUBITEM *tmpSubItem;
2695 INT i;
2697 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2698 if (!lpSubItem) return FALSE;
2699 /* we could binary search here, if need be...*/
2700 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2702 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2703 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2705 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2707 COMCTL32_Free(lpSubItem);
2708 return FALSE;
2710 lpSubItem->iSubItem = lpLVItem->iSubItem;
2711 bModified = TRUE;
2714 if (lpLVItem->mask & LVIF_IMAGE)
2715 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
2717 lpSubItem->hdr.iImage = lpLVItem->iImage;
2718 bModified = TRUE;
2721 if (lpLVItem->mask & LVIF_TEXT)
2722 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
2724 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2725 bModified = TRUE;
2728 if (bModified && !infoPtr->bIsDrawing)
2730 RECT rect;
2732 rect.top = lpLVItem->iSubItem;
2733 rect.left = LVIR_BOUNDS;
2734 /* GetSubItemRect will fail in non-report mode, so there's
2735 * gonna be no invalidation then, yay! */
2736 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2737 LISTVIEW_InvalidateRect(infoPtr, &rect);
2740 return TRUE;
2743 /***
2744 * DESCRIPTION:
2745 * Sets item attributes.
2747 * PARAMETER(S):
2748 * [I] infoPtr : valid pointer to the listview structure
2749 * [I] LPLVITEM : new item atttributes
2750 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2752 * RETURN:
2753 * SUCCESS : TRUE
2754 * FAILURE : FALSE
2756 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2758 INT oldFocus = infoPtr->nFocusedItem;
2759 LPWSTR pszText = NULL;
2760 BOOL bResult;
2762 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2764 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
2765 return FALSE;
2767 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2768 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
2770 pszText = lpLVItem->pszText;
2771 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
2774 /* actually set the fields */
2775 if (infoPtr->dwStyle & LVS_OWNERDATA)
2776 bResult = set_owner_item(infoPtr, lpLVItem, TRUE);
2777 else
2779 /* sanity checks first */
2780 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
2782 if (lpLVItem->iSubItem)
2783 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
2784 else
2785 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
2788 /* redraw item, if necessary */
2789 if (bResult && !infoPtr->bIsDrawing && lpLVItem->iSubItem == 0)
2791 if (oldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
2792 LISTVIEW_ShowFocusRect(infoPtr, oldFocus, FALSE);
2793 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
2795 /* restore text */
2796 if (pszText)
2798 textfreeT(lpLVItem->pszText, isW);
2799 lpLVItem->pszText = pszText;
2802 return bResult;
2805 /***
2806 * DESCRIPTION:
2807 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2809 * PARAMETER(S):
2810 * [I] infoPtr : valid pointer to the listview structure
2812 * RETURN:
2813 * item index
2815 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2817 LONG lStyle = infoPtr->dwStyle;
2818 UINT uView = lStyle & LVS_TYPEMASK;
2819 INT nItem = 0;
2820 SCROLLINFO scrollInfo;
2822 scrollInfo.cbSize = sizeof(SCROLLINFO);
2823 scrollInfo.fMask = SIF_POS;
2825 if (uView == LVS_LIST)
2827 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2828 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2830 else if (uView == LVS_REPORT)
2832 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2833 nItem = scrollInfo.nPos;
2835 else
2837 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2838 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
2841 TRACE("nItem=%d\n", nItem);
2843 return nItem;
2846 /* used by the drawing code */
2847 typedef struct tagTEXTATTR
2849 int bkMode;
2850 COLORREF bkColor;
2851 COLORREF fgColor;
2852 } TEXTATTR;
2854 /* helper function for the drawing code */
2855 static inline void set_text_attr(HDC hdc, TEXTATTR *ta)
2857 ta->bkMode = SetBkMode(hdc, ta->bkMode);
2858 ta->bkColor = SetBkColor(hdc, ta->bkColor);
2859 ta->fgColor = SetTextColor(hdc, ta->fgColor);
2862 /* helper function for the drawing code */
2863 static void select_text_attr(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL isSelected, TEXTATTR *ta)
2865 ta->bkMode = OPAQUE;
2867 if (isSelected && infoPtr->bFocus)
2869 ta->bkColor = comctl32_color.clrHighlight;
2870 ta->fgColor = comctl32_color.clrHighlightText;
2872 else if (isSelected && (infoPtr->dwStyle & LVS_SHOWSELALWAYS))
2874 ta->bkColor = comctl32_color.clr3dFace;
2875 ta->fgColor = comctl32_color.clrBtnText;
2877 else if ( (infoPtr->clrTextBk != CLR_DEFAULT) && (infoPtr->clrTextBk != CLR_NONE) )
2879 ta->bkColor = infoPtr->clrTextBk;
2880 ta->fgColor = infoPtr->clrText;
2882 else
2884 ta->bkMode = TRANSPARENT;
2885 ta->bkColor = GetBkColor(hdc);
2886 ta->fgColor = infoPtr->clrText;
2889 set_text_attr(hdc, ta);
2892 /***
2893 * DESCRIPTION:
2894 * Erases the background of the given rectangle
2896 * PARAMETER(S):
2897 * [I] infoPtr : valid pointer to the listview structure
2898 * [I] hdc : device context handle
2899 * [I] lprcBox : clipping rectangle
2901 * RETURN:
2902 * Success: TRUE
2903 * Failure: FALSE
2905 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
2907 if (!infoPtr->hBkBrush) return FALSE;
2909 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
2911 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
2914 /***
2915 * DESCRIPTION:
2916 * Draws a subitem.
2918 * PARAMETER(S):
2919 * [I] infoPtr : valid pointer to the listview structure
2920 * [I] HDC : device context handle
2921 * [I] INT : item index
2922 * [I] INT : subitem index
2923 * [I] RECT * : clipping rectangle
2925 * RETURN:
2926 * Success: TRUE
2927 * Failure: FALSE
2929 static BOOL LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem,
2930 INT nSubItem, RECT rcItem, UINT align)
2932 WCHAR szDispText[DISP_TEXT_SIZE];
2933 LVITEMW lvItem;
2935 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, rcItem=%s)\n",
2936 hdc, nItem, nSubItem, debugrect(&rcItem));
2938 /* get information needed for drawing the item */
2939 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
2940 lvItem.iItem = nItem;
2941 lvItem.iSubItem = nSubItem;
2942 lvItem.cchTextMax = COUNTOF(szDispText);
2943 lvItem.pszText = szDispText;
2944 *lvItem.pszText = '\0';
2945 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
2947 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2949 if (lvItem.iImage) FIXME("Draw the image for the subitem\n");
2951 DrawTextW(hdc, lvItem.pszText, -1, &rcItem,
2952 DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | align);
2954 return TRUE;
2958 /***
2959 * DESCRIPTION:
2960 * Draws an item.
2962 * PARAMETER(S):
2963 * [I] infoPtr : valid pointer to the listview structure
2964 * [I] hdc : device context handle
2965 * [I] nItem : item index
2966 * [I] rcItem : item rectangle
2968 * RETURN:
2969 * TRUE: if item is focused
2970 * FALSE: otherwise
2972 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
2974 WCHAR szDispText[DISP_TEXT_SIZE];
2975 INT nLabelWidth, imagePadding = 0;
2976 RECT* lprcFocus, rcOrig = rcItem;
2977 LVITEMW lvItem;
2978 TEXTATTR ta;
2980 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
2982 /* get information needed for drawing the item */
2983 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2984 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
2985 lvItem.iItem = nItem;
2986 lvItem.iSubItem = 0;
2987 lvItem.cchTextMax = DISP_TEXT_SIZE;
2988 lvItem.pszText = szDispText;
2989 *lvItem.pszText = '\0';
2990 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
2991 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2993 /* now check if we need to update the focus rectangle */
2994 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
2995 if (lprcFocus) SetRectEmpty(lprcFocus);
2997 /* do indent */
2998 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3000 /* state icons */
3001 if (infoPtr->himlState)
3003 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3004 if (uStateImage)
3006 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3007 rcItem.left, rcItem.top, ILD_NORMAL);
3009 rcItem.left += infoPtr->iconStateSize.cx;
3010 imagePadding = IMAGE_PADDING;
3013 /* small icons */
3014 if (infoPtr->himlSmall)
3016 if (lvItem.iImage >= 0)
3018 UINT mode = (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ?
3019 ILD_SELECTED : ILD_NORMAL;
3020 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3021 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc,
3022 rcItem.left, rcItem.top, mode);
3024 rcItem.left += infoPtr->iconSize.cx;
3025 imagePadding = IMAGE_PADDING;
3028 /* Don't bother painting item being edited */
3029 if (infoPtr->bEditing && lprcFocus)
3030 return FALSE;
3032 select_text_attr(infoPtr, hdc, lvItem.state & LVIS_SELECTED, &ta);
3034 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
3035 rcItem.left += imagePadding;
3036 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3037 if (rcItem.right > rcOrig.right) rcItem.right = rcOrig.right;
3039 if (lvItem.pszText)
3041 TRACE("drawing text=%s, in rect=%s\n", debugstr_w(lvItem.pszText), debugrect(&rcItem));
3042 if(lprcFocus) *lprcFocus = rcItem;
3043 if (lvItem.state & LVIS_SELECTED)
3044 ExtTextOutW(hdc, rcItem.left, rcItem.top, ETO_OPAQUE, &rcItem, 0, 0, 0);
3045 DrawTextW(hdc, lvItem.pszText, -1, &rcItem,
3046 DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | DT_CENTER);
3049 set_text_attr(hdc, &ta);
3050 return lprcFocus != NULL;
3053 /***
3054 * DESCRIPTION:
3055 * Draws an item when in large icon display mode.
3057 * PARAMETER(S):
3058 * [I] infoPtr : valid pointer to the listview structure
3059 * [I] hdc : device context handle
3060 * [I] nItem : item index
3061 * [I] rcItem : clipping rectangle
3063 * RETURN:
3064 * TRUE: if item is focused
3065 * FALSE: otherwise
3067 static BOOL LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
3069 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3070 LVITEMW lvItem;
3071 UINT uFormat = LISTVIEW_DTFLAGS;
3072 RECT rcIcon, rcFocus, rcLabel, *lprcFocus;
3073 POINT ptOrg;
3075 TRACE("(hdc=%x, nItem=%d, rcItem=%s)\n", hdc, nItem, debugrect(&rcItem));
3077 /* get information needed for drawing the item */
3078 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3079 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3080 lvItem.iItem = nItem;
3081 lvItem.iSubItem = 0;
3082 lvItem.cchTextMax = DISP_TEXT_SIZE;
3083 lvItem.pszText = szDispText;
3084 *lvItem.pszText = '\0';
3085 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3086 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3088 /* now check if we need to update the focus rectangle */
3089 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3091 LISTVIEW_GetItemMeasures(infoPtr, nItem, &ptOrg, NULL, &rcIcon, &rcLabel);
3093 /* Set the item to the boundary box for now */
3094 TRACE("iconSize.cx=%ld, nItemWidth=%d\n", infoPtr->iconSize.cx, infoPtr->nItemWidth);
3095 TRACE("rcList=%s, rcView=%s\n", debugrect(&infoPtr->rcList), debugrect(&infoPtr->rcView));
3097 /* Figure out text colours etc. depending on state
3098 * At least the following states exist; there may be more.
3099 * Many items may be selected
3100 * At most one item may have the focus
3101 * The application may not actually be active currently
3102 * 1. The item is not selected in any way
3103 * 2. The cursor is flying over the icon or text and the text is being
3104 * expanded because it is not fully displayed currently.
3105 * 3. The item is selected and is focussed, i.e. the user has not clicked
3106 * in the blank area of the window, and the window (or application?)
3107 * still has the focus.
3108 * 4. As 3 except that a different window has the focus
3109 * 5. The item is the selected item of all the items, but the user has
3110 * clicked somewhere else on the window.
3111 * Only a few of these are handled currently. In particular 2 is not yet
3112 * handled since we do not support the functionality currently (or at least
3113 * we didn't when I wrote this)
3116 if (lvItem.state & LVIS_SELECTED)
3118 /* set item colors */
3119 SetBkColor(hdc, comctl32_color.clrHighlight);
3120 SetTextColor(hdc, comctl32_color.clrHighlightText);
3121 SetBkMode (hdc, OPAQUE);
3122 /* set raster mode */
3123 SetROP2(hdc, R2_XORPEN);
3124 /* When exactly is it in XOR? while being dragged? */
3126 else
3128 /* set item colors */
3129 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3131 SetBkMode(hdc, TRANSPARENT);
3133 else
3135 SetBkMode(hdc, OPAQUE);
3136 SetBkColor(hdc, infoPtr->clrTextBk);
3139 SetTextColor(hdc, infoPtr->clrText);
3140 /* set raster mode */
3141 SetROP2(hdc, R2_COPYPEN);
3144 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3145 * wrapping and long words split.
3146 * In cases 1 and 4 only a portion of the text is displayed with word
3147 * wrapping and both word and end ellipsis. (I don't yet know about path
3148 * ellipsis)
3150 uFormat |= lprcFocus ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3152 /* state icons */
3153 if (infoPtr->himlState != NULL)
3155 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3156 INT x, y;
3158 x = rcIcon.left - infoPtr->iconStateSize.cx + 10;
3159 y = rcIcon.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
3160 if (uStateImage > 0)
3162 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, x,
3163 y, ILD_NORMAL);
3167 /* draw the icon */
3168 if (infoPtr->himlNormal != NULL)
3170 if (lvItem.iImage >= 0)
3172 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc,
3173 rcIcon.left+ICON_LR_HALF,
3174 rcIcon.top+ICON_TOP_PADDING_HITABLE,
3175 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3176 TRACE("icon %d at (%d,%d)\n",
3177 lvItem.iImage, rcIcon.left+ICON_LR_HALF,
3178 rcIcon.top+ICON_TOP_PADDING_HITABLE);
3182 /* Draw the text below the icon */
3184 /* Don't bother painting item being edited */
3185 if ((infoPtr->bEditing && lprcFocus) || !lvItem.pszText || !lstrlenW(lvItem.pszText))
3187 if(lprcFocus) SetRectEmpty(lprcFocus);
3188 return FALSE;
3191 /* draw label */
3193 /* I am sure of most of the uFormat values. However I am not sure about
3194 * whether we need or do not need the following:
3195 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3196 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3197 * We certainly do not need
3198 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3199 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3202 /* If the text is being drawn without clipping (i.e. the full text) then we
3203 * need to jump through a few hoops to ensure that it all gets displayed and
3204 * that the background is complete
3206 rcFocus = rcLabel; /* save for focus */
3207 if (lvItem.state & LVIS_SELECTED)
3208 ExtTextOutW(hdc, rcLabel.left, rcLabel.top, ETO_OPAQUE, &rcLabel, 0, 0, 0);
3209 /* else ? What if we are losing the focus? will we not get a complete
3210 * background?
3213 DrawTextW (hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3214 TRACE("text at rcLabel=%s is %s\n", debugrect(&rcLabel), debugstr_w(lvItem.pszText));
3216 if(lprcFocus) CopyRect(lprcFocus, &rcFocus);
3218 return lprcFocus != NULL;
3221 /***
3222 * DESCRIPTION:
3223 * Draws listview items when in report display mode.
3225 * PARAMETER(S):
3226 * [I] infoPtr : valid pointer to the listview structure
3227 * [I] HDC : device context handle
3229 * RETURN:
3230 * None
3232 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3234 INT rgntype, nDrawPosY, j;
3235 INT nTop, nItem, nLast, nUpdateHeight, nUpdateWidth;
3236 INT nColumnCount, nFirstCol, nLastCol;
3237 RECT rcItem, rcClip, rcFullSelect;
3238 BOOL bFullSelected, isFocused;
3239 DWORD cditemmode = CDRF_DODEFAULT;
3240 LONG lStyle = infoPtr->dwStyle;
3241 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3242 TEXTATTR tmpTa, oldTa;
3243 COLUMNCACHE *lpCols;
3244 LVCOLUMNW lvColumn;
3245 LVITEMW item;
3246 POINT ptOrig;
3248 TRACE("()\n");
3250 /* figure out what to draw */
3251 rgntype = GetClipBox(hdc, &rcClip);
3252 if (rgntype == NULLREGION) return;
3253 nUpdateHeight = rcClip.bottom - rcClip.top + 1;
3254 nUpdateWidth = rcClip.right - rcClip.left;
3255 nTop = LISTVIEW_GetTopIndex(infoPtr);
3256 nItem = nTop + (rcClip.top - infoPtr->rcList.top) / infoPtr->nItemHeight;
3257 if (nItem < nTop)
3258 nItem = nTop;
3259 nLast = nItem + nUpdateHeight / infoPtr->nItemHeight;
3260 if (nUpdateHeight % infoPtr->nItemHeight) nLast++;
3261 if (nLast > infoPtr->nItemCount)
3262 nLast = infoPtr->nItemCount;
3264 /* send cache hint notification */
3265 if (lStyle & LVS_OWNERDATA)
3266 notify_odcachehint(infoPtr, nItem, nLast);
3268 /* cache column info */
3269 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3270 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3271 if (!lpCols) return;
3272 for (j = 0; j < nColumnCount; j++)
3274 Header_GetItemRect(infoPtr->hwndHeader, j, &lpCols[j].rc);
3275 TRACE("lpCols[%d].rc=%s\n", j, debugrect(&lpCols[j].rc));
3278 /* Get scroll info once before loop */
3279 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrig)) return;
3281 /* we now narrow the columns as well */
3282 nLastCol = nColumnCount - 1;
3283 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3284 if (lpCols[nFirstCol].rc.right + ptOrig.x >= rcClip.left) break;
3285 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3286 if (lpCols[nLastCol].rc.left + ptOrig.x < rcClip.right) break;
3288 /* cache the per-column information before we start drawing */
3289 for (j = nFirstCol; j <= nLastCol; j++)
3291 lvColumn.mask = LVCF_FMT;
3292 LISTVIEW_GetColumnT(infoPtr, j, &lvColumn, TRUE);
3293 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3294 lpCols[j].align = DT_LEFT;
3295 if (lvColumn.fmt & LVCFMT_RIGHT)
3296 lpCols[j].align = DT_RIGHT;
3297 else if (lvColumn.fmt & LVCFMT_CENTER)
3298 lpCols[j].align = DT_CENTER;
3301 /* a last few bits before we start drawing */
3302 TRACE("nTop=%d, nItem=%d, nLast=%d, nFirstCol=%d, nLastCol=%d\n",
3303 nTop, nItem, nLast, nFirstCol, nLastCol);
3304 bFullSelected = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT;
3305 nDrawPosY = infoPtr->rcList.top + (nItem - nTop) * infoPtr->nItemHeight;
3307 /* save dc values we're gonna trash while drawing */
3308 oldTa.bkMode = GetBkMode(hdc);
3309 oldTa.bkColor = GetBkColor(hdc);
3310 oldTa.fgColor = GetTextColor(hdc);
3312 /* iterate through the invalidated rows */
3313 for (; nItem < nLast; nItem++, nDrawPosY += infoPtr->nItemHeight)
3315 /* if owner wants to take a first stab at it, have it his way... */
3316 if (lStyle & LVS_OWNERDRAWFIXED)
3318 DRAWITEMSTRUCT dis;
3320 TRACE("Owner Drawn\n");
3322 item.iItem = nItem;
3323 item.iSubItem = 0;
3324 item.mask = LVIF_PARAM | LVIF_STATE;
3325 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3326 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3328 ZeroMemory(&dis, sizeof(dis));
3329 dis.CtlType = ODT_LISTVIEW;
3330 dis.CtlID = uID;
3331 dis.itemID = nItem;
3332 dis.itemAction = ODA_DRAWENTIRE;
3333 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3334 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3335 dis.hwndItem = infoPtr->hwndSelf;
3336 dis.hDC = hdc;
3337 dis.rcItem.left = lpCols[0].rc.left;
3338 dis.rcItem.right = lpCols[nColumnCount - 1].rc.right;
3339 dis.rcItem.top = nDrawPosY;
3340 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3341 OffsetRect(&dis.rcItem, ptOrig.x, 0);
3342 dis.itemData = item.lParam;
3344 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3345 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3346 /* In theory we should do the default drawing if WM_DRAWITEM
3347 * returns FALSE but, in the words of Larry McVoy, in practice
3348 * theory is different than practice, and hence there are
3349 * important apps out there that depend on no default drawing
3350 * in LVS_OWNERDRAWFIXED. So we always skip to the next item. */
3351 continue;
3354 /* compute the full select rectangle, if needed */
3355 if (bFullSelected)
3357 item.mask = LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3358 item.stateMask = LVIS_SELECTED;
3359 item.iItem = nItem;
3360 item.iSubItem = 0;
3361 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3363 rcFullSelect.left = lpCols[0].rc.left + REPORT_MARGINX +
3364 infoPtr->iconSize.cx * item.iIndent +
3365 (infoPtr->himlSmall ? infoPtr->iconSize.cx : 0);
3366 rcFullSelect.right = max(rcFullSelect.left, lpCols[nColumnCount - 1].rc.right - REPORT_MARGINX);
3367 rcFullSelect.top = nDrawPosY;
3368 rcFullSelect.bottom = rcFullSelect.top + infoPtr->nItemHeight;
3369 OffsetRect(&rcFullSelect, ptOrig.x, 0);
3372 /* draw the background of the selection rectangle, if need be */
3373 select_text_attr(infoPtr, hdc, bFullSelected && (item.state & LVIS_SELECTED), &tmpTa);
3374 if (bFullSelected && (item.state & LVIS_SELECTED))
3375 ExtTextOutW(hdc, rcFullSelect.left, rcFullSelect.top, ETO_OPAQUE, &rcFullSelect, 0, 0, 0);
3377 /* iterate through the invalidated columns */
3378 isFocused = FALSE;
3379 for (j = nFirstCol; j <= nLastCol; j++)
3381 if (cdmode & CDRF_NOTIFYITEMDRAW)
3382 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, j, CDDS_ITEMPREPAINT);
3383 if (cditemmode & CDRF_SKIPDEFAULT) continue;
3385 rcItem = lpCols[j].rc;
3386 rcItem.left += REPORT_MARGINX;
3387 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3388 rcItem.top = nDrawPosY;
3389 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3391 /* Offset the Scroll Bar Pos */
3392 OffsetRect(&rcItem, ptOrig.x, 0);
3394 if (j == 0)
3395 isFocused = LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3396 else
3397 LISTVIEW_DrawSubItem(infoPtr, hdc, nItem, j, rcItem, lpCols[j].align);
3399 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3400 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3403 /* Adjust focus if we have it, and we are in full select */
3404 if (bFullSelected && isFocused) infoPtr->rcFocus = rcFullSelect;
3407 /* cleanup the mess */
3408 set_text_attr(hdc, &oldTa);
3409 COMCTL32_Free(lpCols);
3412 /***
3413 * DESCRIPTION:
3414 * Draws listview items when in list display mode.
3416 * PARAMETER(S):
3417 * [I] infoPtr : valid pointer to the listview structure
3418 * [I] HDC : device context handle
3420 * RETURN:
3421 * None
3423 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3425 RECT rcItem;
3426 INT i, j;
3427 INT nItem;
3428 INT nColumnCount;
3429 INT nCountPerColumn;
3430 INT nItemWidth = infoPtr->nItemWidth;
3431 INT nItemHeight = infoPtr->nItemHeight;
3432 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3433 DWORD cditemmode = CDRF_DODEFAULT;
3435 /* get number of fully visible columns */
3436 nColumnCount = nListWidth / nItemWidth;
3437 if (nListWidth % nItemWidth) nColumnCount++;
3438 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
3439 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3440 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3441 nColumnCount, nCountPerColumn, nItem);
3443 for (i = 0; i < nColumnCount; i++)
3445 for (j = 0; j < nCountPerColumn; j++, nItem++)
3447 if (nItem >= infoPtr->nItemCount)
3448 return;
3450 if (cdmode & CDRF_NOTIFYITEMDRAW)
3451 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, 0, CDDS_ITEMPREPAINT);
3452 if (cditemmode & CDRF_SKIPDEFAULT)
3453 continue;
3455 rcItem.top = j * nItemHeight;
3456 rcItem.left = i * nItemWidth;
3457 rcItem.bottom = rcItem.top + nItemHeight;
3458 rcItem.right = rcItem.left + nItemWidth;
3460 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3462 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3463 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3469 /***
3470 * DESCRIPTION:
3471 * Draws listview items when in icon or small icon display mode.
3473 * PARAMETER(S):
3474 * [I] infoPtr : valid pointer to the listview structure
3475 * [I] HDC : device context handle
3477 * RETURN:
3478 * None
3480 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL bSmall, DWORD cdmode)
3482 POINT ptPosition;
3483 POINT ptOrigin;
3484 RECT rcItem, rcClip, rcTemp;
3485 INT i;
3486 DWORD cditemmode = CDRF_DODEFAULT;
3488 TRACE("\n");
3490 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return;
3492 GetClipBox(hdc, &rcClip);
3494 /* Draw the visible non-selected items */
3495 for (i = 0; i < infoPtr->nItemCount; i++)
3497 if (LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3498 continue;
3500 rcItem.left = LVIR_BOUNDS;
3501 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3502 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3503 continue;
3505 if (cdmode & CDRF_NOTIFYITEMDRAW)
3506 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3507 if (cditemmode & CDRF_SKIPDEFAULT)
3508 continue;
3510 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3511 ptPosition.x += ptOrigin.x;
3512 ptPosition.y += ptOrigin.y;
3514 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3516 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3518 if (ptPosition.y < infoPtr->rcList.bottom)
3520 if (ptPosition.x < infoPtr->rcList.right)
3522 rcItem.top = ptPosition.y;
3523 rcItem.left = ptPosition.x;
3524 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3525 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3527 if (bSmall)
3528 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3529 else
3530 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3535 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3536 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3539 /* Draw the visible selected items */
3540 for (i = 0; i < infoPtr->nItemCount; i++)
3542 if (!LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3543 continue;
3545 rcItem.left = LVIR_BOUNDS;
3546 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3547 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3548 continue;
3550 if (cdmode & CDRF_NOTIFYITEMDRAW)
3551 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3552 if (cditemmode & CDRF_SKIPDEFAULT)
3553 continue;
3555 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3556 ptPosition.x += ptOrigin.x;
3557 ptPosition.y += ptOrigin.y;
3559 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3561 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3563 if (ptPosition.y < infoPtr->rcList.bottom)
3565 if (ptPosition.x < infoPtr->rcList.right)
3567 rcItem.top = ptPosition.y;
3568 rcItem.left = ptPosition.x;
3569 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3570 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3572 if (bSmall)
3573 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3574 else
3575 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3580 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3581 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3585 /***
3586 * DESCRIPTION:
3587 * Draws listview items.
3589 * PARAMETER(S):
3590 * [I] infoPtr : valid pointer to the listview structure
3591 * [I] HDC : device context handle
3593 * RETURN:
3594 * NoneX
3596 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3598 UINT uView = LISTVIEW_GetType(infoPtr);
3599 HFONT hOldFont;
3600 DWORD cdmode;
3601 RECT rcClient;
3603 LISTVIEW_DUMP(infoPtr);
3605 GetClientRect(infoPtr->hwndSelf, &rcClient);
3607 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, hdc, rcClient);
3608 if (cdmode == CDRF_SKIPDEFAULT) return;
3610 infoPtr->bIsDrawing = TRUE;
3612 /* nothing to draw */
3613 if(infoPtr->nItemCount == 0) goto enddraw;
3615 /* select font */
3616 hOldFont = SelectObject(hdc, infoPtr->hFont);
3618 if (uView == LVS_LIST)
3619 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3620 else if (uView == LVS_REPORT)
3621 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3622 else
3623 LISTVIEW_RefreshIcon(infoPtr, hdc, uView == LVS_SMALLICON, cdmode);
3625 /* if we have a focus rect, draw it */
3626 if (infoPtr->bFocus && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
3627 DrawFocusRect(hdc, &infoPtr->rcFocus);
3629 /* unselect objects */
3630 SelectObject(hdc, hOldFont);
3632 enddraw:
3633 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3634 notify_customdraw(infoPtr, CDDS_POSTPAINT, hdc, rcClient);
3636 infoPtr->bIsDrawing = FALSE;
3640 /***
3641 * DESCRIPTION:
3642 * Calculates the approximate width and height of a given number of items.
3644 * PARAMETER(S):
3645 * [I] infoPtr : valid pointer to the listview structure
3646 * [I] INT : number of items
3647 * [I] INT : width
3648 * [I] INT : height
3650 * RETURN:
3651 * Returns a DWORD. The width in the low word and the height in high word.
3653 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3654 WORD wWidth, WORD wHeight)
3656 UINT uView = LISTVIEW_GetType(infoPtr);
3657 INT nItemCountPerColumn = 1;
3658 INT nColumnCount = 0;
3659 DWORD dwViewRect = 0;
3661 if (nItemCount == -1)
3662 nItemCount = infoPtr->nItemCount;
3664 if (uView == LVS_LIST)
3666 if (wHeight == 0xFFFF)
3668 /* use current height */
3669 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3672 if (wHeight < infoPtr->nItemHeight)
3673 wHeight = infoPtr->nItemHeight;
3675 if (nItemCount > 0)
3677 if (infoPtr->nItemHeight > 0)
3679 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3680 if (nItemCountPerColumn == 0)
3681 nItemCountPerColumn = 1;
3683 if (nItemCount % nItemCountPerColumn != 0)
3684 nColumnCount = nItemCount / nItemCountPerColumn;
3685 else
3686 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3690 /* Microsoft padding magic */
3691 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3692 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3694 dwViewRect = MAKELONG(wWidth, wHeight);
3696 else if (uView == LVS_REPORT)
3697 FIXME("uView == LVS_REPORT: not implemented\n");
3698 else if (uView == LVS_SMALLICON)
3699 FIXME("uView == LVS_SMALLICON: not implemented\n");
3700 else if (uView == LVS_ICON)
3701 FIXME("uView == LVS_ICON: not implemented\n");
3703 return dwViewRect;
3706 /***
3707 * DESCRIPTION:
3708 * Arranges listview items in icon display mode.
3710 * PARAMETER(S):
3711 * [I] infoPtr : valid pointer to the listview structure
3712 * [I] INT : alignment code
3714 * RETURN:
3715 * SUCCESS : TRUE
3716 * FAILURE : FALSE
3718 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3720 UINT uView = LISTVIEW_GetType(infoPtr);
3721 BOOL bResult = FALSE;
3723 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3725 switch (nAlignCode)
3727 case LVA_ALIGNLEFT:
3728 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3729 break;
3730 case LVA_ALIGNTOP:
3731 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3732 break;
3733 case LVA_DEFAULT:
3734 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3735 break;
3736 case LVA_SNAPTOGRID:
3737 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3738 break;
3742 return bResult;
3745 /* << LISTVIEW_CreateDragImage >> */
3748 /***
3749 * DESCRIPTION:
3750 * Removes all listview items and subitems.
3752 * PARAMETER(S):
3753 * [I] infoPtr : valid pointer to the listview structure
3755 * RETURN:
3756 * SUCCESS : TRUE
3757 * FAILURE : FALSE
3759 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3761 LONG lStyle = infoPtr->dwStyle;
3762 UINT uView = lStyle & LVS_TYPEMASK;
3763 LISTVIEW_ITEM *lpItem;
3764 LISTVIEW_SUBITEM *lpSubItem;
3765 NMLISTVIEW nmlv;
3766 BOOL bSuppress;
3767 BOOL bResult = FALSE;
3768 HDPA hdpaSubItems;
3770 TRACE("()\n");
3772 LISTVIEW_RemoveAllSelections(infoPtr);
3773 infoPtr->nSelectionMark=-1;
3774 infoPtr->nFocusedItem=-1;
3775 /* But we are supposed to leave nHotItem as is! */
3777 if (lStyle & LVS_OWNERDATA)
3779 infoPtr->nItemCount = 0;
3780 LISTVIEW_InvalidateList(infoPtr);
3781 return TRUE;
3784 if (infoPtr->nItemCount > 0)
3786 INT i, j;
3788 /* send LVN_DELETEALLITEMS notification */
3789 /* verify if subsequent LVN_DELETEITEM notifications should be
3790 suppressed */
3791 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3792 nmlv.iItem = -1;
3793 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3795 for (i = 0; i < infoPtr->nItemCount; i++)
3797 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3798 if (hdpaSubItems != NULL)
3800 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3802 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3803 if (lpSubItem != NULL)
3805 /* free subitem string */
3806 if (is_textW(lpSubItem->hdr.pszText))
3807 COMCTL32_Free(lpSubItem->hdr.pszText);
3809 /* free subitem */
3810 COMCTL32_Free(lpSubItem);
3814 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3815 if (lpItem != NULL)
3817 if (!bSuppress)
3819 /* send LVN_DELETEITEM notification */
3820 nmlv.iItem = i;
3821 nmlv.lParam = lpItem->lParam;
3822 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3825 /* free item string */
3826 if (is_textW(lpItem->hdr.pszText))
3827 COMCTL32_Free(lpItem->hdr.pszText);
3829 /* free item */
3830 COMCTL32_Free(lpItem);
3833 DPA_Destroy(hdpaSubItems);
3837 /* reinitialize listview memory */
3838 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3839 infoPtr->nItemCount = 0;
3841 /* align items (set position of each item) */
3842 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3844 if (lStyle & LVS_ALIGNLEFT)
3846 LISTVIEW_AlignLeft(infoPtr);
3848 else
3850 LISTVIEW_AlignTop(infoPtr);
3854 LISTVIEW_UpdateScroll(infoPtr);
3856 LISTVIEW_InvalidateList(infoPtr);
3859 return bResult;
3862 /***
3863 * DESCRIPTION:
3864 * Removes a column from the listview control.
3866 * PARAMETER(S):
3867 * [I] infoPtr : valid pointer to the listview structure
3868 * [I] INT : column index
3870 * RETURN:
3871 * SUCCESS : TRUE
3872 * FAILURE : FALSE
3874 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3876 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3877 RECT rcCol, rcOld;
3879 TRACE("nColumn=%d\n", nColumn);
3881 if (nColumn <= 0) return FALSE;
3883 if (uView == LVS_REPORT)
3885 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3886 return FALSE;
3888 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3889 return FALSE;
3892 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3894 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3895 HDPA hdpaSubItems;
3896 INT nItem, nSubItem, i;
3898 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3900 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3901 if (!hdpaSubItems) continue;
3902 nSubItem = 0;
3903 lpDelItem = 0;
3904 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3906 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3907 if (!lpSubItem) break;
3908 if (lpSubItem->iSubItem == nColumn)
3910 nSubItem = i;
3911 lpDelItem = lpSubItem;
3913 else if (lpSubItem->iSubItem > nColumn)
3915 lpSubItem->iSubItem--;
3919 /* if we found our subitem, zapp it */
3920 if (nSubItem > 0)
3922 /* free string */
3923 if (is_textW(lpDelItem->hdr.pszText))
3924 COMCTL32_Free(lpDelItem->hdr.pszText);
3926 /* free item */
3927 COMCTL32_Free(lpDelItem);
3929 /* free dpa memory */
3930 DPA_DeletePtr(hdpaSubItems, nSubItem);
3935 /* we need to worry about display issues in report mode only */
3936 if (uView != LVS_REPORT) return TRUE;
3938 /* Need to reset the item width when deleting a column */
3939 infoPtr->nItemWidth -= rcCol.right - rcCol.left;
3941 /* update scrollbar(s) */
3942 LISTVIEW_UpdateScroll(infoPtr);
3944 /* scroll to cover the deleted column, and invalidate for redraw */
3945 rcOld = infoPtr->rcList;
3946 rcOld.left = rcCol.left;
3947 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
3948 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3950 return TRUE;
3953 /***
3954 * DESCRIPTION:
3955 * Removes an item from the listview control.
3957 * PARAMETER(S):
3958 * [I] infoPtr : valid pointer to the listview structure
3959 * [I] INT : item index
3961 * RETURN:
3962 * SUCCESS : TRUE
3963 * FAILURE : FALSE
3965 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3967 LONG lStyle = infoPtr->dwStyle;
3968 UINT uView = lStyle & LVS_TYPEMASK;
3969 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3970 NMLISTVIEW nmlv;
3971 BOOL bResult = FALSE;
3972 HDPA hdpaSubItems;
3973 LISTVIEW_ITEM *lpItem;
3974 LISTVIEW_SUBITEM *lpSubItem;
3975 INT i;
3976 LVITEMW item;
3978 TRACE("(nItem=%d)\n", nItem);
3981 /* First, send LVN_DELETEITEM notification. */
3982 memset(&nmlv, 0, sizeof (NMLISTVIEW));
3983 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
3984 nmlv.hdr.idFrom = lCtrlId;
3985 nmlv.hdr.code = LVN_DELETEITEM;
3986 nmlv.iItem = nItem;
3987 SendMessageW((infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
3988 (LPARAM)&nmlv);
3991 /* remove it from the selection range */
3992 item.state = LVIS_SELECTED;
3993 item.stateMask = LVIS_SELECTED;
3994 LISTVIEW_SetItemState(infoPtr,nItem,&item);
3996 if (lStyle & LVS_OWNERDATA)
3998 infoPtr->nItemCount--;
3999 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
4000 return TRUE;
4003 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
4005 /* initialize memory */
4006 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4008 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4009 if (hdpaSubItems != NULL)
4011 infoPtr->nItemCount--;
4012 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4014 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4015 if (lpSubItem != NULL)
4017 /* free item string */
4018 if (is_textW(lpSubItem->hdr.pszText))
4019 COMCTL32_Free(lpSubItem->hdr.pszText);
4021 /* free item */
4022 COMCTL32_Free(lpSubItem);
4026 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4027 if (lpItem != NULL)
4029 /* free item string */
4030 if (is_textW(lpItem->hdr.pszText))
4031 COMCTL32_Free(lpItem->hdr.pszText);
4033 /* free item */
4034 COMCTL32_Free(lpItem);
4037 bResult = DPA_Destroy(hdpaSubItems);
4040 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
4042 /* align items (set position of each item) */
4043 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4045 if (lStyle & LVS_ALIGNLEFT)
4046 LISTVIEW_AlignLeft(infoPtr);
4047 else
4048 LISTVIEW_AlignTop(infoPtr);
4051 LISTVIEW_UpdateScroll(infoPtr);
4053 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
4056 return bResult;
4060 /***
4061 * DESCRIPTION:
4062 * Callback implementation for editlabel control
4064 * PARAMETER(S):
4065 * [I] infoPtr : valid pointer to the listview structure
4066 * [I] pszText : modified text
4067 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4069 * RETURN:
4070 * SUCCESS : TRUE
4071 * FAILURE : FALSE
4073 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4075 NMLVDISPINFOW dispInfo;
4077 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4079 infoPtr->bEditing = FALSE;
4081 ZeroMemory(&dispInfo, sizeof(dispInfo));
4082 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4083 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4084 dispInfo.item.iSubItem = 0;
4085 dispInfo.item.stateMask = ~0;
4086 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4087 dispInfo.item.pszText = pszText;
4088 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4090 /* Do we need to update the Item Text */
4091 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4092 if (!pszText) return TRUE;
4094 ZeroMemory(&dispInfo, sizeof(dispInfo));
4095 dispInfo.item.mask = LVIF_TEXT;
4096 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4097 dispInfo.item.iSubItem = 0;
4098 dispInfo.item.pszText = pszText;
4099 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4100 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4103 /***
4104 * DESCRIPTION:
4105 * Begin in place editing of specified list view item
4107 * PARAMETER(S):
4108 * [I] infoPtr : valid pointer to the listview structure
4109 * [I] INT : item index
4110 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4112 * RETURN:
4113 * SUCCESS : TRUE
4114 * FAILURE : FALSE
4116 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4118 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4119 NMLVDISPINFOW dispInfo;
4120 RECT rect;
4122 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4124 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4126 infoPtr->nEditLabelItem = nItem;
4128 /* Is the EditBox still there, if so remove it */
4129 if(infoPtr->hwndEdit != 0)
4131 SetFocus(infoPtr->hwndSelf);
4132 infoPtr->hwndEdit = 0;
4135 LISTVIEW_SetSelection(infoPtr, nItem);
4136 LISTVIEW_SetItemFocus(infoPtr, nItem);
4138 rect.left = LVIR_LABEL;
4139 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4141 ZeroMemory(&dispInfo, sizeof(dispInfo));
4142 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4143 dispInfo.item.iItem = nItem;
4144 dispInfo.item.iSubItem = 0;
4145 dispInfo.item.stateMask = ~0;
4146 dispInfo.item.pszText = szDispText;
4147 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4148 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4150 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4151 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4152 if (!infoPtr->hwndEdit) return 0;
4154 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4156 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4157 infoPtr->hwndEdit = 0;
4158 return 0;
4161 infoPtr->bEditing = TRUE;
4162 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4163 SetFocus(infoPtr->hwndEdit);
4164 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4165 return infoPtr->hwndEdit;
4169 /***
4170 * DESCRIPTION:
4171 * Ensures the specified item is visible, scrolling into view if necessary.
4173 * PARAMETER(S):
4174 * [I] infoPtr : valid pointer to the listview structure
4175 * [I] nItem : item index
4176 * [I] bPartial : partially or entirely visible
4178 * RETURN:
4179 * SUCCESS : TRUE
4180 * FAILURE : FALSE
4182 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4184 UINT uView = LISTVIEW_GetType(infoPtr);
4185 INT nScrollPosHeight = 0;
4186 INT nScrollPosWidth = 0;
4187 INT nPartialAdjust = 0;
4188 INT nHorzDiff = 0;
4189 INT nVertDiff = 0;
4190 RECT rcItem;
4192 /* FIXME: ALWAYS bPartial == FALSE, FOR NOW! */
4194 rcItem.left = LVIR_BOUNDS;
4195 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4197 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4199 /* scroll left/right, but in LVS_REPORT mode */
4200 if (uView == LVS_LIST)
4201 nScrollPosWidth = infoPtr->nItemWidth;
4202 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4203 nScrollPosWidth = 1;
4205 if (rcItem.left < infoPtr->rcList.left)
4207 nPartialAdjust = -1;
4208 if (uView != LVS_REPORT) nHorzDiff = rcItem.left + infoPtr->rcList.left;
4210 else
4212 nPartialAdjust = 1;
4213 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4217 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4219 /* scroll up/down, but not in LVS_LIST mode */
4220 if (uView == LVS_REPORT)
4221 nScrollPosHeight = infoPtr->nItemHeight;
4222 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4223 nScrollPosHeight = 1;
4225 if (rcItem.top < infoPtr->rcList.top)
4227 nPartialAdjust = -1;
4228 if (uView != LVS_LIST) nVertDiff = rcItem.top + infoPtr->rcList.top;
4230 else
4232 nPartialAdjust = 1;
4233 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4237 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4239 if (nScrollPosWidth)
4241 INT diff = nHorzDiff / nScrollPosWidth;
4242 if (rcItem.left % nScrollPosWidth) diff += nPartialAdjust;
4243 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4246 if (nScrollPosHeight)
4248 INT diff = nVertDiff / nScrollPosHeight;
4249 if (rcItem.top % nScrollPosHeight) diff += nPartialAdjust;
4250 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4253 return TRUE;
4256 /***
4257 * DESCRIPTION:
4258 * Retrieves the nearest item, given a position and a direction.
4260 * PARAMETER(S):
4261 * [I] infoPtr : valid pointer to the listview structure
4262 * [I] POINT : start position
4263 * [I] UINT : direction
4265 * RETURN:
4266 * Item index if successdful, -1 otherwise.
4268 static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDirection)
4270 LVHITTESTINFO ht;
4271 RECT rcView;
4273 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4274 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4275 ((vkDirection == VK_UP) ? "VK_UP" :
4276 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4278 if (!LISTVIEW_GetViewRect(infoPtr, &rcView)) return -1;
4280 if (!LISTVIEW_GetOrigin(infoPtr, &ht.pt)) return -1;
4282 ht.pt.x += pt.x;
4283 ht.pt.y += pt.y;
4285 if (vkDirection == VK_DOWN) ht.pt.y += infoPtr->nItemHeight;
4286 else if (vkDirection == VK_UP) ht.pt.y -= infoPtr->nItemHeight;
4287 else if (vkDirection == VK_LEFT) ht.pt.x -= infoPtr->nItemWidth;
4288 else if (vkDirection == VK_RIGHT) ht.pt.x += infoPtr->nItemWidth;
4290 if (!PtInRect(&rcView, ht.pt)) return -1;
4292 return LISTVIEW_SuperHitTestItem(infoPtr, &ht, TRUE, TRUE);
4295 /***
4296 * DESCRIPTION:
4297 * Searches for an item with specific characteristics.
4299 * PARAMETER(S):
4300 * [I] hwnd : window handle
4301 * [I] nStart : base item index
4302 * [I] lpFindInfo : item information to look for
4304 * RETURN:
4305 * SUCCESS : index of item
4306 * FAILURE : -1
4308 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4309 LPLVFINDINFOW lpFindInfo)
4311 POINT ptItem;
4312 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4313 LVITEMW lvItem;
4314 BOOL bWrap = FALSE;
4315 INT nItem = nStart;
4316 INT nLast = infoPtr->nItemCount;
4318 if ((nItem >= -1) && (lpFindInfo != NULL))
4320 lvItem.mask = 0;
4321 if (lpFindInfo->flags & LVFI_PARAM)
4323 lvItem.mask |= LVIF_PARAM;
4326 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4328 lvItem.mask |= LVIF_TEXT;
4329 lvItem.pszText = szDispText;
4330 lvItem.cchTextMax = DISP_TEXT_SIZE;
4333 if (lpFindInfo->flags & LVFI_WRAP)
4334 bWrap = TRUE;
4336 if (lpFindInfo->flags & LVFI_NEARESTXY)
4338 ptItem.x = lpFindInfo->pt.x;
4339 ptItem.y = lpFindInfo->pt.y;
4342 while (1)
4344 while (nItem < nLast)
4346 if (lpFindInfo->flags & LVFI_NEARESTXY)
4348 nItem = LISTVIEW_GetNearestItem(infoPtr, ptItem,
4349 lpFindInfo->vkDirection);
4350 if (nItem != -1)
4352 /* get position of the new item index */
4353 if (!ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &ptItem))
4354 return -1;
4356 else
4357 return -1;
4359 else
4361 nItem++;
4364 lvItem.iItem = nItem;
4365 lvItem.iSubItem = 0;
4366 if (LISTVIEW_GetItemW(infoPtr, &lvItem))
4368 if (lvItem.mask & LVIF_TEXT)
4370 if (lpFindInfo->flags & LVFI_PARTIAL)
4372 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4373 continue;
4375 else
4377 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4378 continue;
4382 if (lvItem.mask & LVIF_PARAM)
4384 if (lpFindInfo->lParam != lvItem.lParam)
4385 continue;
4388 return nItem;
4392 if (bWrap)
4394 nItem = -1;
4395 nLast = nStart + 1;
4396 bWrap = FALSE;
4398 else
4400 return -1;
4405 return -1;
4408 /***
4409 * DESCRIPTION:
4410 * Searches for an item with specific characteristics.
4412 * PARAMETER(S):
4413 * [I] hwnd : window handle
4414 * [I] nStart : base item index
4415 * [I] lpFindInfo : item information to look for
4417 * RETURN:
4418 * SUCCESS : index of item
4419 * FAILURE : -1
4421 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4422 LPLVFINDINFOA lpFindInfo)
4424 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4425 LVFINDINFOW fiw;
4426 LRESULT res;
4428 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4429 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4430 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4431 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4432 return res;
4435 /***
4436 * DESCRIPTION:
4437 * Retrieves the background image of the listview control.
4439 * PARAMETER(S):
4440 * [I] infoPtr : valid pointer to the listview structure
4441 * [O] LPLVMKBIMAGE : background image attributes
4443 * RETURN:
4444 * SUCCESS : TRUE
4445 * FAILURE : FALSE
4447 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4448 /* { */
4449 /* FIXME (listview, "empty stub!\n"); */
4450 /* return FALSE; */
4451 /* } */
4453 /***
4454 * DESCRIPTION:
4455 * Retrieves column attributes.
4457 * PARAMETER(S):
4458 * [I] infoPtr : valid pointer to the listview structure
4459 * [I] INT : column index
4460 * [IO] LPLVCOLUMNW : column information
4461 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4462 * otherwise it is in fact a LPLVCOLUMNA
4464 * RETURN:
4465 * SUCCESS : TRUE
4466 * FAILURE : FALSE
4468 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4470 HDITEMW hdi;
4471 BOOL bResult = FALSE;
4473 if (lpColumn != NULL)
4476 /* initialize memory */
4477 ZeroMemory(&hdi, sizeof(hdi));
4479 if (lpColumn->mask & LVCF_FMT)
4480 hdi.mask |= HDI_FORMAT;
4482 if (lpColumn->mask & LVCF_WIDTH)
4483 hdi.mask |= HDI_WIDTH;
4485 if (lpColumn->mask & LVCF_TEXT)
4487 hdi.mask |= HDI_TEXT;
4488 hdi.cchTextMax = lpColumn->cchTextMax;
4489 hdi.pszText = lpColumn->pszText;
4492 if (lpColumn->mask & LVCF_IMAGE)
4493 hdi.mask |= HDI_IMAGE;
4495 if (lpColumn->mask & LVCF_ORDER)
4496 hdi.mask |= HDI_ORDER;
4498 if (isW)
4499 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4500 else
4501 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4503 if (bResult)
4505 if (lpColumn->mask & LVCF_FMT)
4507 lpColumn->fmt = 0;
4509 if (hdi.fmt & HDF_LEFT)
4510 lpColumn->fmt |= LVCFMT_LEFT;
4511 else if (hdi.fmt & HDF_RIGHT)
4512 lpColumn->fmt |= LVCFMT_RIGHT;
4513 else if (hdi.fmt & HDF_CENTER)
4514 lpColumn->fmt |= LVCFMT_CENTER;
4516 if (hdi.fmt & HDF_IMAGE)
4517 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4519 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4520 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4523 if (lpColumn->mask & LVCF_WIDTH)
4524 lpColumn->cx = hdi.cxy;
4526 if (lpColumn->mask & LVCF_IMAGE)
4527 lpColumn->iImage = hdi.iImage;
4529 if (lpColumn->mask & LVCF_ORDER)
4530 lpColumn->iOrder = hdi.iOrder;
4532 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4533 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4538 return bResult;
4542 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4544 INT i;
4546 if (!lpiArray)
4547 return FALSE;
4549 /* FIXME: little hack */
4550 for (i = 0; i < iCount; i++)
4551 lpiArray[i] = i;
4553 return TRUE;
4556 /***
4557 * DESCRIPTION:
4558 * Retrieves the column width.
4560 * PARAMETER(S):
4561 * [I] infoPtr : valid pointer to the listview structure
4562 * [I] int : column index
4564 * RETURN:
4565 * SUCCESS : column width
4566 * FAILURE : zero
4568 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4570 INT nColumnWidth = 0;
4571 HDITEMW hdi;
4573 TRACE("nColumn=%d\n", nColumn);
4575 switch(LISTVIEW_GetType(infoPtr))
4577 case LVS_LIST:
4578 nColumnWidth = infoPtr->nItemWidth;
4579 break;
4580 case LVS_REPORT:
4581 hdi.mask = HDI_WIDTH;
4582 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4583 nColumnWidth = hdi.cxy;
4584 break;
4585 default:
4586 /* we don't have a 'column' in [SMALL]ICON mode */
4589 TRACE("nColumnWidth=%d\n", nColumnWidth);
4590 return nColumnWidth;
4593 /***
4594 * DESCRIPTION:
4595 * In list or report display mode, retrieves the number of items that can fit
4596 * vertically in the visible area. In icon or small icon display mode,
4597 * retrieves the total number of visible items.
4599 * PARAMETER(S):
4600 * [I] infoPtr : valid pointer to the listview structure
4602 * RETURN:
4603 * Number of fully visible items.
4605 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4607 UINT uView = LISTVIEW_GetType(infoPtr);
4608 INT nItemCount = 0;
4610 if (uView == LVS_LIST)
4612 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4614 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4615 LISTVIEW_GetCountPerColumn(infoPtr);
4618 else if (uView == LVS_REPORT)
4620 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4622 else
4624 nItemCount = infoPtr->nItemCount;
4627 return nItemCount;
4631 /***
4632 * DESCRIPTION:
4633 * Retrieves an image list handle.
4635 * PARAMETER(S):
4636 * [I] infoPtr : valid pointer to the listview structure
4637 * [I] nImageList : image list identifier
4639 * RETURN:
4640 * SUCCESS : image list handle
4641 * FAILURE : NULL
4643 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4645 HIMAGELIST himl = NULL;
4647 switch (nImageList)
4649 case LVSIL_NORMAL:
4650 himl = infoPtr->himlNormal;
4651 break;
4652 case LVSIL_SMALL:
4653 himl = infoPtr->himlSmall;
4654 break;
4655 case LVSIL_STATE:
4656 himl = infoPtr->himlState;
4657 break;
4660 return (LRESULT)himl;
4663 /* LISTVIEW_GetISearchString */
4665 /***
4666 * Helper function for LISTVIEW_GetItemT *only*. Tests if an item is selected.
4667 * It is important that no other functions call this because of callbacks.
4669 static inline BOOL is_item_selected(LISTVIEW_INFO *infoPtr, INT nItem)
4671 RANGE selection = { nItem, nItem };
4673 return DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
4674 LISTVIEW_CompareSelectionRanges, 0, DPAS_SORTED) != -1;
4677 /***
4678 * DESCRIPTION:
4679 * Retrieves item attributes.
4681 * PARAMETER(S):
4682 * [I] hwnd : window handle
4683 * [IO] lpLVItem : item info
4684 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4685 * if FALSE, the lpLVItem is a LPLVITEMA.
4687 * NOTE:
4688 * This is the internal 'GetItem' interface -- it tries to
4689 * be smart, and avoids text copies, if possible, by modifing
4690 * lpLVItem->pszText to point to the text string. Please note
4691 * that this is not always possible (e.g. OWNERDATA), so on
4692 * entry you *must* supply valid values for pszText, and cchTextMax.
4693 * The only difference to the documented interface is that upon
4694 * return, you should use *only* the lpLVItem->pszText, rather than
4695 * the buffer pointer you provided on input. Most code already does
4696 * that, so it's not a problem.
4697 * For the two cases when the text must be copied (that is,
4698 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4700 * RETURN:
4701 * SUCCESS : TRUE
4702 * FAILURE : FALSE
4704 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4706 NMLVDISPINFOW dispInfo;
4707 LISTVIEW_ITEM *lpItem;
4708 ITEMHDR* pItemHdr;
4709 HDPA hdpaSubItems;
4711 /* In the following:
4712 * lpLVItem describes the information requested by the user
4713 * lpItem is what we have
4714 * dispInfo is a structure we use to request the missing
4715 * information from the application
4718 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4720 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4721 (lpLVItem->iItem >= infoPtr->nItemCount))
4722 return FALSE;
4724 /* a quick optimization if all we're asked is the focus state
4725 * these queries are worth optimising since they are common,
4726 * and can be answered in constant time, without the heavy accesses */
4727 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4728 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4730 lpLVItem->state = 0;
4731 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4732 lpLVItem->state |= LVIS_FOCUSED;
4733 return TRUE;
4736 ZeroMemory(&dispInfo, sizeof(dispInfo));
4738 /* if the app stores all the data, handle it separately */
4739 if (infoPtr->dwStyle & LVS_OWNERDATA)
4741 dispInfo.item.state = 0;
4743 /* if we need to callback, do it now */
4744 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4746 /* NOTE: copy only fields which we _know_ are initialized, some apps
4747 * depend on the uninitialized fields being 0 */
4748 dispInfo.item.mask = lpLVItem->mask;
4749 dispInfo.item.iItem = lpLVItem->iItem;
4750 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4751 if (lpLVItem->mask & LVIF_TEXT)
4753 dispInfo.item.pszText = lpLVItem->pszText;
4754 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4756 if (lpLVItem->mask & LVIF_STATE)
4757 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4758 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4759 dispInfo.item.stateMask = lpLVItem->stateMask;
4760 *lpLVItem = dispInfo.item;
4761 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4764 /* we store only a little state, so if we're not asked, we're done */
4765 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4767 /* if focus is handled by us, report it */
4768 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4770 lpLVItem->state &= ~LVIS_FOCUSED;
4771 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4772 lpLVItem->state |= LVIS_FOCUSED;
4775 /* and do the same for selection, if we handle it */
4776 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4778 lpLVItem->state &= ~LVIS_SELECTED;
4779 if (is_item_selected(infoPtr, lpLVItem->iItem))
4780 lpLVItem->state |= LVIS_SELECTED;
4783 return TRUE;
4786 /* find the item and subitem structures before we proceed */
4787 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4788 if (hdpaSubItems == NULL) return FALSE;
4790 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4791 return FALSE;
4793 if (lpLVItem->iSubItem)
4795 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4796 if(!lpSubItem) return FALSE;
4797 pItemHdr = &lpSubItem->hdr;
4799 else
4800 pItemHdr = &lpItem->hdr;
4802 /* Do we need to query the state from the app? */
4803 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4805 dispInfo.item.mask |= LVIF_STATE;
4806 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4809 /* Do we need to enquire about the image? */
4810 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4811 dispInfo.item.mask |= LVIF_IMAGE;
4813 /* Do we need to enquire about the text? */
4814 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4816 dispInfo.item.mask |= LVIF_TEXT;
4817 dispInfo.item.pszText = lpLVItem->pszText;
4818 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4819 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4820 *dispInfo.item.pszText = '\0';
4823 /* If we don't have all the requested info, query the application */
4824 if (dispInfo.item.mask != 0)
4826 dispInfo.item.iItem = lpLVItem->iItem;
4827 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4828 dispInfo.item.lParam = lpItem->lParam;
4829 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4830 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4833 /* Now, handle the iImage field */
4834 if (dispInfo.item.mask & LVIF_IMAGE)
4836 lpLVItem->iImage = dispInfo.item.iImage;
4837 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4838 pItemHdr->iImage = dispInfo.item.iImage;
4840 else if (lpLVItem->mask & LVIF_IMAGE)
4841 lpLVItem->iImage = pItemHdr->iImage;
4843 /* The pszText field */
4844 if (dispInfo.item.mask & LVIF_TEXT)
4846 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4847 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4849 lpLVItem->pszText = dispInfo.item.pszText;
4851 else if (lpLVItem->mask & LVIF_TEXT)
4853 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4854 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4857 /* if this is a subitem, we're done*/
4858 if (lpLVItem->iSubItem) return TRUE;
4860 /* Next is the lParam field */
4861 if (dispInfo.item.mask & LVIF_PARAM)
4863 lpLVItem->lParam = dispInfo.item.lParam;
4864 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4865 lpItem->lParam = dispInfo.item.lParam;
4867 else if (lpLVItem->mask & LVIF_PARAM)
4868 lpLVItem->lParam = lpItem->lParam;
4870 /* ... the state field (this one is different due to uCallbackmask) */
4871 if (lpLVItem->mask & LVIF_STATE)
4873 lpLVItem->state = lpItem->state;
4874 if (dispInfo.item.mask & LVIF_STATE)
4876 lpLVItem->state &= ~dispInfo.item.stateMask;
4877 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4879 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4881 lpLVItem->state &= ~LVIS_FOCUSED;
4882 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4883 lpLVItem->state |= LVIS_FOCUSED;
4885 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4887 lpLVItem->state &= ~LVIS_SELECTED;
4888 if (is_item_selected(infoPtr, lpLVItem->iItem))
4889 lpLVItem->state |= LVIS_SELECTED;
4893 /* and last, but not least, the indent field */
4894 if (lpLVItem->mask & LVIF_INDENT)
4895 lpLVItem->iIndent = lpItem->iIndent;
4897 return TRUE;
4900 /***
4901 * DESCRIPTION:
4902 * Retrieves item attributes.
4904 * PARAMETER(S):
4905 * [I] hwnd : window handle
4906 * [IO] lpLVItem : item info
4907 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4908 * if FALSE, the lpLVItem is a LPLVITEMA.
4910 * NOTE:
4911 * This is the external 'GetItem' interface -- it properly copies
4912 * the text in the provided buffer.
4914 * RETURN:
4915 * SUCCESS : TRUE
4916 * FAILURE : FALSE
4918 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4920 LPWSTR pszText;
4921 BOOL bResult;
4923 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4924 return FALSE;
4926 pszText = lpLVItem->pszText;
4927 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4928 if (bResult && lpLVItem->pszText != pszText)
4929 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4930 lpLVItem->pszText = pszText;
4932 return bResult;
4936 /***
4937 * DESCRIPTION:
4938 * Retrieves the position (upper-left) of the listview control item.
4939 * Note that for LVS_ICON style, the upper-left is that of the icon
4940 * and not the bounding box.
4942 * PARAMETER(S):
4943 * [I] infoPtr : valid pointer to the listview structure
4944 * [I] INT : item index
4945 * [O] LPPOINT : coordinate information
4947 * RETURN:
4948 * SUCCESS : TRUE
4949 * FAILURE : FALSE
4951 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4953 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4955 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4957 return LISTVIEW_GetItemMeasures(infoPtr, nItem, lpptPosition, NULL, NULL, NULL);
4961 /***
4962 * DESCRIPTION: [INTERNAL]
4963 * Update the bounding rectangle around the text under a large icon.
4964 * This depends on whether it has the focus or not.
4965 * On entry the rectangle's top, left and right should be set.
4966 * On return the bottom will also be set and the width may have been
4967 * modified.
4969 * PARAMETER
4970 * [I] infoPtr : pointer to the listview structure
4971 * [I] nItem : the item for which we are calculating this
4972 * [I/O] rect : the rectangle to be updated
4974 * This appears to be weird, even in the Microsoft implementation.
4976 static BOOL LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *infoPtr, int nItem, RECT *rect)
4978 HDC hdc = GetDC (infoPtr->hwndSelf);
4979 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
4980 UINT uFormat = LISTVIEW_DTFLAGS | DT_CALCRECT;
4981 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4982 RECT rcText = *rect;
4983 RECT rcBack = *rect;
4984 BOOL focused, selected;
4985 int dx, dy, old_wid, new_wid;
4986 LVITEMW lvItem;
4988 TRACE("%s, focus item=%d, cur item=%d\n",
4989 (infoPtr->bFocus) ? "Window has focus" : "Window not focused",
4990 infoPtr->nFocusedItem, nItem);
4993 focused = infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED);
4994 selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4996 uFormat |= (focused) ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
4998 /* We (aim to) display the full text. In Windows 95 it appears to
4999 * calculate the size assuming the specified font and then it draws
5000 * the text in that region with the specified font except scaled to
5001 * 10 point (or the height of the system font or ...). Thus if the
5002 * window has 24 point Helvetica the highlit rectangle will be
5003 * taller than the text and if it is 7 point Helvetica then the text
5004 * will be clipped.
5005 * For now we will simply say that it is the correct size to display
5006 * the text in the specified font.
5008 lvItem.mask = LVIF_TEXT;
5009 lvItem.iItem = nItem;
5010 lvItem.iSubItem = 0;
5011 lvItem.pszText = szDispText;
5012 lvItem.cchTextMax = DISP_TEXT_SIZE;
5013 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5015 InflateRect(&rcText, -2, 0);
5016 DrawTextW (hdc, lvItem.pszText, -1, &rcText, uFormat);
5017 /* Microsoft, in their great wisdom, have decided that the rectangle
5018 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
5019 * not the location. So we have to do the centring ourselves (and take
5020 * responsibility for agreeing off-by-one consistency with them).
5023 old_wid = rcText.right - rcText.left;
5024 new_wid = rcBack.right - rcBack.left;
5025 dx = rcBack.left - rcText.left + (new_wid-old_wid)/2;
5026 dy = rcBack.top - rcText.top;
5027 OffsetRect (&rcText, dx, dy);
5029 if (focused)
5031 rcText.bottom += 2;
5032 InflateRect(&rcText, 2, 0);
5034 else /* not focused, may or may not be selected */
5037 * We need to have the bottom to be an intergal number of
5038 * text lines (ntmHeight) below text top that is less than
5039 * or equal to the nItemHeight.
5041 INT lh = infoPtr->nItemHeight - infoPtr->iconSize.cy -
5042 ICON_TOP_PADDING - ICON_BOTTOM_PADDING;
5043 INT ih = (lh / infoPtr->ntmHeight) * infoPtr->ntmHeight;
5044 rcText.bottom = min(rcText.bottom, rcText.top + ih);
5045 rcText.bottom += 1;
5047 *rect = rcText;
5049 TRACE("%s and %s, bounding rect=(%d,%d)-(%d,%d)\n",
5050 (focused) ? "focused(full text)" : "not focused",
5051 (selected) ? "selected" : "not selected",
5052 rect->left, rect->top, rect->right, rect->bottom);
5054 SelectObject (hdc, hOldFont);
5055 ReleaseDC (infoPtr->hwndSelf, hdc);
5057 return TRUE;
5060 /***
5061 * DESCRIPTION:
5062 * Retrieves the bounding rectangle for a listview control item.
5064 * PARAMETER(S):
5065 * [I] infoPtr : valid pointer to the listview structure
5066 * [I] nItem : item index
5067 * [IO] lprc : bounding rectangle coordinates
5068 * lprc->left specifies the portion of the item for which the bounding
5069 * rectangle will be retrieved.
5071 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5072 * including the icon and label.
5074 * * For LVS_ICON
5075 * * Experiment shows that native control returns:
5076 * * width = min (48, length of text line)
5077 * * .left = position.x - (width - iconsize.cx)/2
5078 * * .right = .left + width
5079 * * height = #lines of text * ntmHeight + icon height + 8
5080 * * .top = position.y - 2
5081 * * .bottom = .top + height
5082 * * separation between items .y = itemSpacing.cy - height
5083 * * .x = itemSpacing.cx - width
5084 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5086 * * For LVS_ICON
5087 * * Experiment shows that native control returns:
5088 * * width = iconSize.cx + 16
5089 * * .left = position.x - (width - iconsize.cx)/2
5090 * * .right = .left + width
5091 * * height = iconSize.cy + 4
5092 * * .top = position.y - 2
5093 * * .bottom = .top + height
5094 * * separation between items .y = itemSpacing.cy - height
5095 * * .x = itemSpacing.cx - width
5096 * LVIR_LABEL Returns the bounding rectangle of the item text.
5098 * * For LVS_ICON
5099 * * Experiment shows that native control returns:
5100 * * width = text length
5101 * * .left = position.x - width/2
5102 * * .right = .left + width
5103 * * height = ntmH * linecount + 2
5104 * * .top = position.y + iconSize.cy + 6
5105 * * .bottom = .top + height
5106 * * separation between items .y = itemSpacing.cy - height
5107 * * .x = itemSpacing.cx - width
5108 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5109 * rectangles, but excludes columns in report view.
5111 * RETURN:
5112 * SUCCESS : TRUE
5113 * FAILURE : FALSE
5115 * NOTES
5116 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5117 * upon whether the window has the focus currently and on whether the item
5118 * is the one with the focus. Ensure that the control's record of which
5119 * item has the focus agrees with the items' records.
5121 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5123 RECT label_rect;
5125 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5127 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5129 switch(lprc->left)
5131 case LVIR_ICON:
5132 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, lprc, NULL)) return FALSE;
5133 break;
5135 case LVIR_LABEL:
5136 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, NULL, lprc)) return FALSE;
5137 break;
5139 case LVIR_BOUNDS:
5140 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, NULL)) return FALSE;
5141 break;
5143 case LVIR_SELECTBOUNDS:
5144 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, &label_rect)) return FALSE;
5145 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
5146 (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) )
5147 lprc->right = label_rect.right;
5148 break;
5150 default:
5151 WARN("Unknown value: %d\n", lprc->left);
5152 return FALSE;
5155 TRACE(" rect=%s\n", debugrect(lprc));
5157 return TRUE;
5160 /***
5161 * DESCRIPTION:
5162 * Retrieves the spacing between listview control items.
5164 * PARAMETER(S):
5165 * [I] infoPtr : valid pointer to the listview structure
5166 * [IO] lprc : rectangle to receive the output
5167 * on input, lprc->top = nSubItem
5168 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5170 * NOTE: this call is succeeds only for REPORT style listviews.
5171 * Because we can calculate things much faster in report mode,
5172 * we're gonna do the calculations inline here, instead of
5173 * calling functions that do heavy lifting.
5175 * RETURN:
5176 * TRUE: success
5177 * FALSE: failure
5179 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5181 POINT ptPosition;
5183 if (!lprc || LISTVIEW_GetType(infoPtr) != LVS_REPORT) return FALSE;
5185 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5187 if (!Header_GetItemRect(infoPtr->hwndHeader, lprc->top, lprc)) return FALSE;
5188 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptPosition)) return FALSE;
5189 lprc->top = ptPosition.y;
5190 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5192 switch(lprc->left)
5194 case LVIR_ICON:
5195 FIXME("Unimplemented LVIR_ICON\n");
5196 return FALSE;
5197 case LVIR_LABEL:
5198 case LVIR_BOUNDS:
5199 /* nothing to do here, we're done */
5200 break;
5201 default:
5202 ERR("Unknown bounds=%d\n", lprc->left);
5203 return FALSE;
5205 return TRUE;
5209 /***
5210 * DESCRIPTION:
5211 * Retrieves the width of a label.
5213 * PARAMETER(S):
5214 * [I] infoPtr : valid pointer to the listview structure
5216 * RETURN:
5217 * SUCCESS : string width (in pixels)
5218 * FAILURE : zero
5220 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5222 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5223 LVITEMW lvItem;
5225 TRACE("(nItem=%d)\n", nItem);
5227 lvItem.mask = LVIF_TEXT;
5228 lvItem.iItem = nItem;
5229 lvItem.iSubItem = 0;
5230 lvItem.pszText = szDispText;
5231 lvItem.cchTextMax = DISP_TEXT_SIZE;
5232 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5234 /* FIXME: is this right? What if the label is very long? */
5235 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5238 /***
5239 * DESCRIPTION:
5240 * Retrieves the spacing between listview control items.
5242 * PARAMETER(S):
5243 * [I] infoPtr : valid pointer to the listview structure
5244 * [I] BOOL : flag for small or large icon
5246 * RETURN:
5247 * Horizontal + vertical spacing
5249 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5251 LONG lResult;
5253 if (!bSmall)
5255 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5257 else
5259 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
5260 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5261 else
5262 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5264 return lResult;
5267 /***
5268 * DESCRIPTION:
5269 * Retrieves the state of a listview control item.
5271 * PARAMETER(S):
5272 * [I] infoPtr : valid pointer to the listview structure
5273 * [I] nItem : item index
5274 * [I] uMask : state mask
5276 * RETURN:
5277 * State specified by the mask.
5279 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5281 LVITEMW lvItem;
5283 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5285 lvItem.iItem = nItem;
5286 lvItem.iSubItem = 0;
5287 lvItem.mask = LVIF_STATE;
5288 lvItem.stateMask = uMask;
5289 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5291 return lvItem.state & uMask;
5294 /***
5295 * DESCRIPTION:
5296 * Retrieves the text of a listview control item or subitem.
5298 * PARAMETER(S):
5299 * [I] hwnd : window handle
5300 * [I] nItem : item index
5301 * [IO] lpLVItem : item information
5302 * [I] isW : TRUE if lpLVItem is Unicode
5304 * RETURN:
5305 * SUCCESS : string length
5306 * FAILURE : 0
5308 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5310 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5312 lpLVItem->mask = LVIF_TEXT;
5313 lpLVItem->iItem = nItem;
5314 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5316 return textlenT(lpLVItem->pszText, isW);
5319 /***
5320 * DESCRIPTION:
5321 * Searches for an item based on properties + relationships.
5323 * PARAMETER(S):
5324 * [I] infoPtr : valid pointer to the listview structure
5325 * [I] nItem : item index
5326 * [I] uFlags : relationship flag
5328 * FIXME:
5329 * This function is ver, very inefficient! Needs work.
5331 * RETURN:
5332 * SUCCESS : item index
5333 * FAILURE : -1
5335 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5337 UINT uView = LISTVIEW_GetType(infoPtr);
5338 UINT uMask = 0;
5339 LVFINDINFOW lvFindInfo;
5340 INT nCountPerColumn;
5341 INT i;
5343 TRACE("nItem=%d, uFlags=%x\n", nItem, uFlags);
5345 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5347 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5349 if (uFlags & LVNI_CUT)
5350 uMask |= LVIS_CUT;
5352 if (uFlags & LVNI_DROPHILITED)
5353 uMask |= LVIS_DROPHILITED;
5355 if (uFlags & LVNI_FOCUSED)
5356 uMask |= LVIS_FOCUSED;
5358 if (uFlags & LVNI_SELECTED)
5359 uMask |= LVIS_SELECTED;
5361 /* if we're asked for the focused item, that's only one,
5362 * so it's worth optimizing */
5363 if (uFlags & LVNI_FOCUSED)
5365 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5366 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5369 if (uFlags & LVNI_ABOVE)
5371 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5373 while (nItem >= 0)
5375 nItem--;
5376 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5377 return nItem;
5380 else
5382 lvFindInfo.flags = LVFI_NEARESTXY;
5383 lvFindInfo.vkDirection = VK_UP;
5384 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5385 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5387 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5388 return nItem;
5392 else if (uFlags & LVNI_BELOW)
5394 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5396 while (nItem < infoPtr->nItemCount)
5398 nItem++;
5399 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5400 return nItem;
5403 else
5405 lvFindInfo.flags = LVFI_NEARESTXY;
5406 lvFindInfo.vkDirection = VK_DOWN;
5407 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5408 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5410 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5411 return nItem;
5415 else if (uFlags & LVNI_TOLEFT)
5417 if (uView == LVS_LIST)
5419 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5420 while (nItem - nCountPerColumn >= 0)
5422 nItem -= nCountPerColumn;
5423 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5424 return nItem;
5427 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5429 lvFindInfo.flags = LVFI_NEARESTXY;
5430 lvFindInfo.vkDirection = VK_LEFT;
5431 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5432 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5434 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5435 return nItem;
5439 else if (uFlags & LVNI_TORIGHT)
5441 if (uView == LVS_LIST)
5443 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5444 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5446 nItem += nCountPerColumn;
5447 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5448 return nItem;
5451 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5453 lvFindInfo.flags = LVFI_NEARESTXY;
5454 lvFindInfo.vkDirection = VK_RIGHT;
5455 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5456 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5458 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5459 return nItem;
5463 else
5465 nItem++;
5467 /* search by index */
5468 for (i = nItem; i < infoPtr->nItemCount; i++)
5470 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5471 return i;
5475 return -1;
5478 /* LISTVIEW_GetNumberOfWorkAreas */
5480 /***
5481 * DESCRIPTION:
5482 * Retrieves the origin coordinates when in icon or small icon display mode.
5484 * PARAMETER(S):
5485 * [I] infoPtr : valid pointer to the listview structure
5486 * [O] lpptOrigin : coordinate information
5488 * RETURN:
5489 * SUCCESS : TRUE
5490 * FAILURE : FALSE
5492 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5494 DWORD lStyle = infoPtr->dwStyle;
5495 UINT uView = lStyle & LVS_TYPEMASK;
5496 INT nHorzPos = 0, nVertPos = 0;
5497 SCROLLINFO scrollInfo;
5499 if (!lpptOrigin) return FALSE;
5501 scrollInfo.cbSize = sizeof(SCROLLINFO);
5502 scrollInfo.fMask = SIF_POS;
5504 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5505 nHorzPos = scrollInfo.nPos;
5506 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5507 nVertPos = scrollInfo.nPos;
5509 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5511 lpptOrigin->x = infoPtr->rcList.left;
5512 lpptOrigin->y = infoPtr->rcList.top;
5513 if (uView == LVS_LIST)
5515 nHorzPos *= LISTVIEW_GetCountPerColumn(infoPtr);
5516 nVertPos = 0;
5518 else if (uView == LVS_REPORT)
5520 nVertPos *= infoPtr->nItemHeight;
5523 lpptOrigin->x -= nHorzPos;
5524 lpptOrigin->y -= nVertPos;
5526 TRACE("(pt=(%ld,%ld))\n", lpptOrigin->x, lpptOrigin->y);
5528 return TRUE;
5531 /***
5532 * DESCRIPTION:
5533 * Retrieves the width of a string.
5535 * PARAMETER(S):
5536 * [I] hwnd : window handle
5537 * [I] lpszText : text string to process
5538 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5540 * RETURN:
5541 * SUCCESS : string width (in pixels)
5542 * FAILURE : zero
5544 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5546 SIZE stringSize;
5548 stringSize.cx = 0;
5549 if (is_textT(lpszText, isW))
5551 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5552 HDC hdc = GetDC(infoPtr->hwndSelf);
5553 HFONT hOldFont = SelectObject(hdc, hFont);
5555 if (isW)
5556 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5557 else
5558 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5559 SelectObject(hdc, hOldFont);
5560 ReleaseDC(infoPtr->hwndSelf, hdc);
5562 return stringSize.cx;
5566 /***
5567 * DESCRIPTION:
5568 * Determines item if a hit or closest if not
5570 * PARAMETER(S):
5571 * [I] infoPtr : valid pointer to the listview structure
5572 * [IO] lpht : hit test information
5573 * [I] subitem : fill out iSubItem.
5574 * [I] bNearItem : return the nearest item
5576 * RETURN:
5577 * SUCCESS : item index of hit
5578 * FAILURE : -1
5580 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL bNearItem)
5582 LONG lStyle = infoPtr->dwStyle;
5583 UINT uView = lStyle & LVS_TYPEMASK;
5584 INT i,j,topindex,bottomindex,nearestItem;
5585 RECT rcItem,rcSubItem;
5586 DWORD xterm, yterm, dist, mindist;
5588 TRACE("(x=%ld, y=%ld)\n", lpht->pt.x, lpht->pt.y);
5590 nearestItem = -1;
5591 mindist = -1;
5593 /* FIXME: get the visible range */
5594 topindex = LISTVIEW_GetTopIndex(infoPtr);
5595 if (uView == LVS_REPORT)
5597 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
5598 bottomindex = min(bottomindex,infoPtr->nItemCount);
5600 else
5602 bottomindex = infoPtr->nItemCount;
5605 for (i = topindex; i < bottomindex; i++)
5607 rcItem.left = LVIR_BOUNDS;
5608 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5610 if (PtInRect(&rcItem, lpht->pt))
5612 rcSubItem = rcItem;
5613 rcItem.left = LVIR_ICON;
5614 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5616 if (PtInRect(&rcItem, lpht->pt))
5618 lpht->flags = LVHT_ONITEMICON;
5619 lpht->iItem = i;
5620 goto set_subitem;
5624 rcItem.left = LVIR_LABEL;
5625 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5627 if (PtInRect(&rcItem, lpht->pt))
5629 lpht->flags = LVHT_ONITEMLABEL;
5630 lpht->iItem = i;
5631 goto set_subitem;
5635 lpht->flags = LVHT_ONITEMSTATEICON;
5636 lpht->iItem = i;
5637 set_subitem:
5638 if (subitem)
5640 INT nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5641 lpht->iSubItem = 0;
5642 rcSubItem.right = rcSubItem.left;
5643 for (j = 0; j < nColumnCount; j++)
5645 rcSubItem.left = rcSubItem.right;
5646 rcSubItem.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5647 if (PtInRect(&rcSubItem, lpht->pt))
5649 lpht->iSubItem = j;
5650 break;
5654 TRACE("hit on item %d\n", i);
5655 return i;
5657 else if (bNearItem)
5660 * Now compute distance from point to center of boundary
5661 * box. Since we are only interested in the relative
5662 * distance, we can skip the nasty square root operation
5664 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpht->pt.x;
5665 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpht->pt.y;
5666 dist = xterm * xterm + yterm * yterm;
5667 if (mindist < 0 || dist < mindist)
5669 mindist = dist;
5670 nearestItem = i;
5676 lpht->flags = LVHT_NOWHERE;
5678 return bNearItem ? nearestItem : -1;
5682 /***
5683 * DESCRIPTION:
5684 * Determines which listview item is located at the specified position.
5686 * PARAMETER(S):
5687 * [I] infoPtr : valid pointer to the listview structure
5688 * [IO] lpht : hit test information
5689 * [I] subitem : fill out iSubItem.
5691 * RETURN:
5692 * SUCCESS : item index
5693 * FAILURE : -1
5695 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem)
5697 TRACE("(x=%ld, y=%ld)\n", lpht->pt.x, lpht->pt.y);
5699 lpht->flags = 0;
5701 if (infoPtr->rcList.left > lpht->pt.x)
5702 lpht->flags |= LVHT_TOLEFT;
5703 else if (infoPtr->rcList.right < lpht->pt.x)
5704 lpht->flags |= LVHT_TORIGHT;
5706 if (infoPtr->rcList.top > lpht->pt.y)
5707 lpht->flags |= LVHT_ABOVE;
5708 else if (infoPtr->rcList.bottom < lpht->pt.y)
5709 lpht->flags |= LVHT_BELOW;
5711 if (lpht->flags) return -1;
5713 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
5714 * an app might pass only a structure with space up to iItem!
5715 * (MS Office 97 does that for instance in the file open dialog)
5717 return LISTVIEW_SuperHitTestItem(infoPtr, lpht, subitem, FALSE);
5721 /***
5722 * DESCRIPTION:
5723 * Inserts a new column.
5725 * PARAMETER(S):
5726 * [I] infoPtr : valid pointer to the listview structure
5727 * [I] INT : column index
5728 * [I] LPLVCOLUMNW : column information
5730 * RETURN:
5731 * SUCCESS : new column index
5732 * FAILURE : -1
5734 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5735 LPLVCOLUMNW lpColumn, BOOL isW)
5737 RECT rcOld, rcCol;
5738 INT nNewColumn;
5739 HDITEMW hdi;
5741 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5743 if (!lpColumn) return -1;
5745 hdi.mask = hdi.fmt = 0;
5746 if (lpColumn->mask & LVCF_FMT)
5748 /* format member is valid */
5749 hdi.mask |= HDI_FORMAT;
5751 /* set text alignment (leftmost column must be left-aligned) */
5752 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5753 hdi.fmt |= HDF_LEFT;
5754 else if (lpColumn->fmt & LVCFMT_RIGHT)
5755 hdi.fmt |= HDF_RIGHT;
5756 else if (lpColumn->fmt & LVCFMT_CENTER)
5757 hdi.fmt |= HDF_CENTER;
5759 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5760 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5762 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5764 hdi.fmt |= HDF_IMAGE;
5765 hdi.iImage = I_IMAGECALLBACK;
5768 if (lpColumn->fmt & LVCFMT_IMAGE)
5769 ; /* FIXME: enable images for *(sub)items* this column */
5772 if (lpColumn->mask & LVCF_WIDTH)
5774 hdi.mask |= HDI_WIDTH;
5775 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5777 /* make it fill the remainder of the controls width */
5778 HDITEMW hdit;
5779 RECT rcHeader;
5780 INT item_index;
5782 /* get the width of every item except the current one */
5783 hdit.mask = HDI_WIDTH;
5784 hdi.cxy = 0;
5786 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5787 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5788 hdi.cxy += hdit.cxy;
5790 /* retrieve the layout of the header */
5791 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5792 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5794 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5796 else
5797 hdi.cxy = lpColumn->cx;
5800 if (lpColumn->mask & LVCF_TEXT)
5802 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5803 hdi.fmt |= HDF_STRING;
5804 hdi.pszText = lpColumn->pszText;
5805 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5808 if (lpColumn->mask & LVCF_IMAGE)
5810 hdi.mask |= HDI_IMAGE;
5811 hdi.iImage = lpColumn->iImage;
5814 if (lpColumn->mask & LVCF_ORDER)
5816 hdi.mask |= HDI_ORDER;
5817 hdi.iOrder = lpColumn->iOrder;
5820 /* insert item in header control */
5821 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5822 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5823 (WPARAM)nColumn, (LPARAM)&hdi);
5824 if (nNewColumn == -1) return -1;
5825 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5827 /* now we have to actually adjust the data */
5828 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5830 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5831 HDPA hdpaSubItems;
5832 INT nItem, i;
5834 /* preallocate memory, so we can fail gracefully */
5835 if (nNewColumn == 0)
5837 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5838 if (!lpNewItems) return -1;
5839 for (i = 0; i < infoPtr->nItemCount; i++)
5840 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5841 if (i != infoPtr->nItemCount)
5843 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5844 COMCTL32_Free(lpNewItems);
5845 return -1;
5849 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5851 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5852 if (!hdpaSubItems) continue;
5853 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5855 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5856 if (!lpSubItem) break;
5857 if (lpSubItem->iSubItem >= nNewColumn)
5858 lpSubItem->iSubItem++;
5861 /* if we found our subitem, zapp it */
5862 if (nNewColumn == 0)
5864 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5865 lpSubItem = lpNewItems[nItem];
5866 lpSubItem->hdr = lpMainItem->hdr;
5867 lpSubItem->iSubItem = 1;
5868 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5869 lpMainItem->iSubItem = 0;
5870 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5874 COMCTL32_Free(lpNewItems);
5877 /* we don't have to worry abiut display issues in non-report mode */
5878 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5880 /* Need to reset the item width when inserting a new column */
5881 infoPtr->nItemWidth += rcCol.right - rcCol.left;
5883 LISTVIEW_UpdateScroll(infoPtr);
5885 /* scroll to cover the deleted column, and invalidate for redraw */
5886 rcOld = infoPtr->rcList;
5887 rcOld.left = rcCol.left;
5888 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5889 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5891 return nNewColumn;
5894 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5895 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5896 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5897 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5898 their own sort proc. when sending LVM_SORTITEMS.
5900 /* Platform SDK:
5901 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5903 LVS_SORTXXX must be specified,
5904 LVS_OWNERDRAW is not set,
5905 <item>.pszText is not LPSTR_TEXTCALLBACK.
5907 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5908 are sorted based on item text..."
5910 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5912 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5913 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5914 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5916 /* if we're sorting descending, negate the return value */
5917 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5920 /***
5921 * nESCRIPTION:
5922 * Inserts a new item in the listview control.
5924 * PARAMETER(S):
5925 * [I] infoPtr : valid pointer to the listview structure
5926 * [I] lpLVItem : item information
5927 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5929 * RETURN:
5930 * SUCCESS : new item index
5931 * FAILURE : -1
5933 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5935 LONG lStyle = infoPtr->dwStyle;
5936 UINT uView = lStyle & LVS_TYPEMASK;
5937 INT nItem = -1;
5938 HDPA hdpaSubItems;
5939 NMLISTVIEW nmlv;
5940 LISTVIEW_ITEM *lpItem;
5941 BOOL is_sorted;
5943 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5945 if (lStyle & LVS_OWNERDATA)
5947 nItem = infoPtr->nItemCount;
5948 infoPtr->nItemCount++;
5949 return nItem;
5952 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5953 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5955 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5957 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5958 return -1;
5960 /* insert item in listview control data structure */
5961 if ( (hdpaSubItems = DPA_Create(8)) )
5962 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5963 if (nItem == -1) goto fail;
5965 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5966 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5968 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5969 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5970 hdpaSubItems );
5971 if (nItem == -1) goto fail;
5972 infoPtr->nItemCount++;
5974 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5976 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5977 infoPtr->nItemCount--;
5978 goto fail;
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 /* Add the subitem list to the items array. Do this last in case we go to
5996 * fail during the above.
5998 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6000 lpItem->valid = TRUE;
6002 /* send LVN_INSERTITEM notification */
6003 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6004 nmlv.iItem = nItem;
6005 nmlv.lParam = lpItem->lParam;
6006 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6008 /* align items (set position of each item) */
6009 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6011 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
6012 else LISTVIEW_AlignTop(infoPtr);
6015 LISTVIEW_UpdateScroll(infoPtr);
6017 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6019 TRACE(" <- %d\n", nItem);
6020 return nItem;
6022 fail:
6023 DPA_DeletePtr(hdpaSubItems, 0);
6024 DPA_Destroy (hdpaSubItems);
6025 COMCTL32_Free (lpItem);
6026 return -1;
6029 /***
6030 * DESCRIPTION:
6031 * Redraws a range of items.
6033 * PARAMETER(S):
6034 * [I] infoPtr : valid pointer to the listview structure
6035 * [I] INT : first item
6036 * [I] INT : last item
6038 * RETURN:
6039 * SUCCESS : TRUE
6040 * FAILURE : FALSE
6042 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6044 INT i;
6046 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6047 max(nFirst, nLast) >= infoPtr->nItemCount)
6048 return FALSE;
6050 for (i = nFirst; i <= nLast; i++)
6051 LISTVIEW_InvalidateItem(infoPtr, i);
6053 return TRUE;
6056 /***
6057 * DESCRIPTION:
6058 * Scroll the content of a listview.
6060 * PARAMETER(S):
6061 * [I] infoPtr : valid pointer to the listview structure
6062 * [I] INT : horizontal scroll amount in pixels
6063 * [I] INT : vertical scroll amount in pixels
6065 * RETURN:
6066 * SUCCESS : TRUE
6067 * FAILURE : FALSE
6069 * COMMENTS:
6070 * If the control is in report mode (LVS_REPORT) the control can
6071 * be scrolled only in line increments. "dy" will be rounded to the
6072 * nearest number of pixels that are a whole line. Ex: if line height
6073 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6074 * is passed the the scroll will be 0. (per MSDN 7/2002)
6076 * For: (per experimentaion with native control and CSpy ListView)
6077 * LVS_ICON dy=1 = 1 pixel (vertical only)
6078 * dx ignored
6079 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6080 * dx ignored
6081 * LVS_LIST dx=1 = 1 column (horizontal only)
6082 * but will only scroll 1 column per message
6083 * no matter what the value.
6084 * dy must be 0 or FALSE returned.
6085 * LVS_REPORT dx=1 = 1 pixel
6086 * dy= see above
6089 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6091 switch(LISTVIEW_GetType(infoPtr)) {
6092 case LVS_REPORT:
6093 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6094 dy /= infoPtr->nItemHeight;
6095 break;
6096 case LVS_LIST:
6097 if (dy != 0) return FALSE;
6098 break;
6099 default: /* icon */
6100 dx = 0;
6101 break;
6104 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6105 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6107 return TRUE;
6110 /***
6111 * DESCRIPTION:
6112 * Sets the background color.
6114 * PARAMETER(S):
6115 * [I] infoPtr : valid pointer to the listview structure
6116 * [I] COLORREF : background color
6118 * RETURN:
6119 * SUCCESS : TRUE
6120 * FAILURE : FALSE
6122 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6124 TRACE("(clrBk=%lx)\n", clrBk);
6126 if(infoPtr->clrBk != clrBk) {
6127 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6128 infoPtr->clrBk = clrBk;
6129 if (clrBk == CLR_NONE)
6130 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6131 else
6132 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6133 LISTVIEW_InvalidateList(infoPtr);
6136 return TRUE;
6139 /* LISTVIEW_SetBkImage */
6141 /***
6142 * DESCRIPTION:
6143 * Sets the attributes of a header item.
6145 * PARAMETER(S):
6146 * [I] infoPtr : valid pointer to the listview structure
6147 * [I] INT : column index
6148 * [I] LPLVCOLUMNW : column attributes
6149 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6150 * otherwise it is in fact a LPLVCOLUMNA
6152 * RETURN:
6153 * SUCCESS : TRUE
6154 * FAILURE : FALSE
6156 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6157 LPLVCOLUMNW lpColumn, BOOL isW)
6159 BOOL bResult = FALSE;
6160 HDITEMW hdi, hdiget;
6162 if ((lpColumn != NULL) && (nColumn >= 0) &&
6163 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6165 /* initialize memory */
6166 ZeroMemory(&hdi, sizeof(hdi));
6168 if (lpColumn->mask & LVCF_FMT)
6170 /* format member is valid */
6171 hdi.mask |= HDI_FORMAT;
6173 /* get current format first */
6174 hdiget.mask = HDI_FORMAT;
6175 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6176 /* preserve HDF_STRING if present */
6177 hdi.fmt = hdiget.fmt & HDF_STRING;
6179 /* set text alignment (leftmost column must be left-aligned) */
6180 if (nColumn == 0)
6182 hdi.fmt |= HDF_LEFT;
6184 else
6186 if (lpColumn->fmt & LVCFMT_LEFT)
6187 hdi.fmt |= HDF_LEFT;
6188 else if (lpColumn->fmt & LVCFMT_RIGHT)
6189 hdi.fmt |= HDF_RIGHT;
6190 else if (lpColumn->fmt & LVCFMT_CENTER)
6191 hdi.fmt |= HDF_CENTER;
6194 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6195 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6197 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6198 hdi.fmt |= HDF_IMAGE;
6200 if (lpColumn->fmt & LVCFMT_IMAGE)
6202 hdi.fmt |= HDF_IMAGE;
6203 hdi.iImage = I_IMAGECALLBACK;
6207 if (lpColumn->mask & LVCF_WIDTH)
6209 hdi.mask |= HDI_WIDTH;
6210 hdi.cxy = lpColumn->cx;
6213 if (lpColumn->mask & LVCF_TEXT)
6215 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6216 hdi.pszText = lpColumn->pszText;
6217 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6218 hdi.fmt |= HDF_STRING;
6221 if (lpColumn->mask & LVCF_IMAGE)
6223 hdi.mask |= HDI_IMAGE;
6224 hdi.iImage = lpColumn->iImage;
6227 if (lpColumn->mask & LVCF_ORDER)
6229 hdi.mask |= HDI_ORDER;
6230 hdi.iOrder = lpColumn->iOrder;
6233 /* set header item attributes */
6234 if (isW)
6235 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6236 else
6237 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6240 return bResult;
6243 /***
6244 * DESCRIPTION:
6245 * Sets the column order array
6247 * PARAMETERS:
6248 * [I] infoPtr : valid pointer to the listview structure
6249 * [I] INT : number of elements in column order array
6250 * [I] INT : pointer to column order array
6252 * RETURN:
6253 * SUCCESS : TRUE
6254 * FAILURE : FALSE
6256 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6258 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6260 if (!lpiArray)
6261 return FALSE;
6263 return TRUE;
6267 /***
6268 * DESCRIPTION:
6269 * Sets the width of a column
6271 * PARAMETERS:
6272 * [I] infoPtr : valid pointer to the listview structure
6273 * [I] INT : column index
6274 * [I] INT : column width
6276 * RETURN:
6277 * SUCCESS : TRUE
6278 * FAILURE : FALSE
6280 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6282 HDITEMW hdi;
6283 LRESULT lret;
6284 LONG lStyle = infoPtr->dwStyle;
6285 UINT uView = lStyle & LVS_TYPEMASK;
6286 HDC hdc;
6287 HFONT header_font;
6288 HFONT old_font;
6289 SIZE size;
6290 WCHAR text_buffer[DISP_TEXT_SIZE];
6291 INT header_item_count;
6292 INT item_index;
6293 INT nLabelWidth;
6294 RECT rcHeader;
6295 LVITEMW lvItem;
6296 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6298 if (!infoPtr->hwndHeader) /* make sure we have a header */
6299 return (FALSE);
6301 /* set column width only if in report or list mode */
6302 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6303 return (FALSE);
6305 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6307 /* take care of invalid cx values */
6308 if((uView == LVS_REPORT) && (cx < -2))
6309 cx = LVSCW_AUTOSIZE;
6310 else if (uView == LVS_LIST && (cx < 1))
6311 return FALSE;
6313 /* resize all columns if in LVS_LIST mode */
6314 if(uView == LVS_LIST) {
6315 infoPtr->nItemWidth = cx;
6316 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6317 return TRUE;
6320 /* autosize based on listview items width */
6321 if(cx == LVSCW_AUTOSIZE)
6323 /* set the width of the column to the width of the widest item */
6324 if (iCol == 0 || uView == LVS_LIST)
6326 cx = 0;
6327 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6329 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6330 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6332 if (infoPtr->himlSmall)
6333 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6335 else
6337 lvItem.iSubItem = iCol;
6338 lvItem.mask = LVIF_TEXT;
6339 lvItem.pszText = szDispText;
6340 lvItem.cchTextMax = DISP_TEXT_SIZE;
6341 cx = 0;
6342 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6344 lvItem.iItem = item_index;
6345 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6346 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6347 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6350 cx += TRAILING_PADDING;
6351 } /* autosize based on listview header width */
6352 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6354 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6356 /* if iCol is the last column make it fill the remainder of the controls width */
6357 if(iCol == (header_item_count - 1)) {
6358 /* get the width of every item except the current one */
6359 hdi.mask = HDI_WIDTH;
6360 cx = 0;
6362 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6363 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6364 cx+=hdi.cxy;
6367 /* retrieve the layout of the header */
6368 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6370 cx = (rcHeader.right - rcHeader.left) - cx;
6372 else
6374 /* Despite what the MS docs say, if this is not the last
6375 column, then MS resizes the column to the width of the
6376 largest text string in the column, including headers
6377 and items. This is different from LVSCW_AUTOSIZE in that
6378 LVSCW_AUTOSIZE ignores the header string length.
6381 /* retrieve header font */
6382 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6384 /* retrieve header text */
6385 hdi.mask = HDI_TEXT;
6386 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6387 hdi.pszText = text_buffer;
6389 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6391 /* determine the width of the text in the header */
6392 hdc = GetDC(infoPtr->hwndSelf);
6393 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6395 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6397 SelectObject(hdc, old_font); /* restore the old font */
6398 ReleaseDC(infoPtr->hwndSelf, hdc);
6400 lvItem.iSubItem = iCol;
6401 lvItem.mask = LVIF_TEXT;
6402 lvItem.pszText = szDispText;
6403 lvItem.cchTextMax = DISP_TEXT_SIZE;
6404 cx = size.cx;
6405 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6407 lvItem.iItem = item_index;
6408 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6409 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6410 nLabelWidth += TRAILING_PADDING;
6411 /* While it is possible for subitems to have icons, even MS messes
6412 up the positioning, so I suspect no applications actually use
6413 them. */
6414 if (item_index == 0 && infoPtr->himlSmall)
6415 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6416 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6421 /* call header to update the column change */
6422 hdi.mask = HDI_WIDTH;
6424 hdi.cxy = cx;
6425 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6427 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6429 return lret;
6432 /***
6433 * DESCRIPTION:
6434 * Sets the extended listview style.
6436 * PARAMETERS:
6437 * [I] infoPtr : valid pointer to the listview structure
6438 * [I] DWORD : mask
6439 * [I] DWORD : style
6441 * RETURN:
6442 * SUCCESS : previous style
6443 * FAILURE : 0
6445 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6447 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6449 /* set new style */
6450 if (dwMask)
6451 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6452 else
6453 infoPtr->dwLvExStyle = dwStyle;
6455 return dwOldStyle;
6458 /***
6459 * DESCRIPTION:
6460 * Sets the new hot cursor used during hot tracking and hover selection.
6462 * PARAMETER(S):
6463 * [I] infoPtr : valid pointer to the listview structure
6464 * [I} hCurosr : the new hot cursor handle
6466 * RETURN:
6467 * Returns the previous hot cursor
6469 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6471 HCURSOR oldCursor = infoPtr->hHotCursor;
6472 infoPtr->hHotCursor = hCursor;
6473 return oldCursor;
6477 /***
6478 * DESCRIPTION:
6479 * Sets the hot item index.
6481 * PARAMETERS:
6482 * [I] infoPtr : valid pointer to the listview structure
6483 * [I] INT : index
6485 * RETURN:
6486 * SUCCESS : previous hot item index
6487 * FAILURE : -1 (no hot item)
6489 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6491 INT iOldIndex = infoPtr->nHotItem;
6492 infoPtr->nHotItem = iIndex;
6493 return iOldIndex;
6497 /***
6498 * DESCRIPTION:
6499 * Sets the amount of time the cursor must hover over an item before it is selected.
6501 * PARAMETER(S):
6502 * [I] infoPtr : valid pointer to the listview structure
6503 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6505 * RETURN:
6506 * Returns the previous hover time
6508 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6510 DWORD oldHoverTime = infoPtr->dwHoverTime;
6511 infoPtr->dwHoverTime = dwHoverTime;
6512 return oldHoverTime;
6515 /***
6516 * DESCRIPTION:
6517 * Sets spacing for icons of LVS_ICON style.
6519 * PARAMETER(S):
6520 * [I] infoPtr : valid pointer to the listview structure
6521 * [I] DWORD : MAKELONG(cx, cy)
6523 * RETURN:
6524 * MAKELONG(oldcx, oldcy)
6526 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6528 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6529 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6530 LONG lStyle = infoPtr->dwStyle;
6531 UINT uView = lStyle & LVS_TYPEMASK;
6533 TRACE("requested=(%d,%d)\n", cx, cy);
6535 /* this is supported only for LVS_ICON style */
6536 if (uView != LVS_ICON) return oldspacing;
6538 /* set to defaults, if instructed to */
6539 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6540 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6542 /* if 0 then compute width
6543 * FIXME: Should scan each item and determine max width of
6544 * icon or label, then make that the width */
6545 if (cx == 0)
6546 cx = infoPtr->iconSpacing.cx;
6548 /* if 0 then compute height */
6549 if (cy == 0)
6550 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6551 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6554 infoPtr->iconSpacing.cx = cx;
6555 infoPtr->iconSpacing.cy = cy;
6557 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6558 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6559 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6560 infoPtr->ntmHeight);
6562 /* these depend on the iconSpacing */
6563 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6564 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6566 return oldspacing;
6569 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6571 INT cx, cy;
6573 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6575 size->cx = cx;
6576 size->cy = cy;
6578 else
6579 size->cx = size->cy = 0;
6582 /***
6583 * DESCRIPTION:
6584 * Sets image lists.
6586 * PARAMETER(S):
6587 * [I] infoPtr : valid pointer to the listview structure
6588 * [I] INT : image list type
6589 * [I] HIMAGELIST : image list handle
6591 * RETURN:
6592 * SUCCESS : old image list
6593 * FAILURE : NULL
6595 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6597 UINT uView = LISTVIEW_GetType(infoPtr);
6598 INT oldHeight = infoPtr->nItemHeight;
6599 HIMAGELIST himlOld = 0;
6601 switch (nType)
6603 case LVSIL_NORMAL:
6604 himlOld = infoPtr->himlNormal;
6605 infoPtr->himlNormal = himl;
6606 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6607 LISTVIEW_SetIconSpacing(infoPtr, 0);
6608 break;
6610 case LVSIL_SMALL:
6611 himlOld = infoPtr->himlSmall;
6612 infoPtr->himlSmall = himl;
6613 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6614 break;
6616 case LVSIL_STATE:
6617 himlOld = infoPtr->himlState;
6618 infoPtr->himlState = himl;
6619 update_icon_size(himl, &infoPtr->iconStateSize);
6620 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6621 break;
6623 default:
6624 ERR("Unknown icon type=%d\n", nType);
6625 return NULL;
6628 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6629 if (infoPtr->nItemHeight != oldHeight)
6630 LISTVIEW_UpdateScroll(infoPtr);
6632 return himlOld;
6635 /***
6636 * DESCRIPTION:
6637 * Preallocates memory (does *not* set the actual count of items !)
6639 * PARAMETER(S):
6640 * [I] infoPtr : valid pointer to the listview structure
6641 * [I] INT : item count (projected number of items to allocate)
6642 * [I] DWORD : update flags
6644 * RETURN:
6645 * SUCCESS : TRUE
6646 * FAILURE : FALSE
6648 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6650 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6652 if (infoPtr->dwStyle & LVS_OWNERDATA)
6654 int precount,topvisible;
6656 TRACE("LVS_OWNERDATA is set!\n");
6657 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6658 FIXME("flags %s %s not implemented\n",
6659 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6660 : "",
6661 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6663 LISTVIEW_RemoveAllSelections(infoPtr);
6665 precount = infoPtr->nItemCount;
6666 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6667 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6669 /* Grow the hdpaItems array if necessary */
6670 if (nItems > infoPtr->hdpaItems->nMaxCount)
6671 if (!DPA_SetPtr(infoPtr->hdpaItems, nItems - 1, NULL))
6672 return FALSE;
6674 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6675 DEFAULT_COLUMN_WIDTH);
6677 LISTVIEW_UpdateSize(infoPtr);
6678 LISTVIEW_UpdateScroll(infoPtr);
6680 if (min(precount,infoPtr->nItemCount)<topvisible)
6681 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6683 else
6685 /* According to MSDN for non-LVS_OWNERDATA this is just
6686 * a performance issue. The control allocates its internal
6687 * data structures for the number of items specified. It
6688 * cuts down on the number of memory allocations. Therefore
6689 * we will just issue a WARN here
6691 WARN("for non-ownerdata performance option not implemented.\n");
6694 return TRUE;
6697 /***
6698 * DESCRIPTION:
6699 * Sets the position of an item.
6701 * PARAMETER(S):
6702 * [I] infoPtr : valid pointer to the listview structure
6703 * [I] INT : item index
6704 * [I] LONG : x coordinate
6705 * [I] LONG : y coordinate
6707 * RETURN:
6708 * SUCCESS : TRUE
6709 * FAILURE : FALSE
6711 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem,
6712 LONG nPosX, LONG nPosY)
6714 UINT lStyle = infoPtr->dwStyle;
6715 UINT uView = lStyle & LVS_TYPEMASK;
6716 LISTVIEW_ITEM *lpItem;
6717 HDPA hdpaSubItems;
6718 BOOL bResult = FALSE;
6720 TRACE("(nItem=%d, X=%ld, Y=%ld)\n", nItem, nPosX, nPosY);
6722 if (lStyle & LVS_OWNERDATA)
6723 return FALSE;
6725 if ((nItem >= 0) || (nItem < infoPtr->nItemCount))
6727 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
6729 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
6731 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
6733 POINT orig;
6734 bResult = TRUE;
6735 orig = lpItem->ptPosition;
6736 if ((nPosX == -1) && (nPosY == -1))
6738 /* This point value seems to be an undocumented feature. The
6739 * best guess is that it means either at the origin, or at
6740 * the true beginning of the list. I will assume the origin.
6742 POINT pt1;
6743 if (!LISTVIEW_GetOrigin(infoPtr, &pt1))
6745 pt1.x = 0;
6746 pt1.y = 0;
6748 nPosX = pt1.x;
6749 nPosY = pt1.y;
6750 if (uView == LVS_ICON)
6752 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6753 nPosY += ICON_TOP_PADDING;
6755 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
6756 nPosX, nPosY);
6759 lpItem->ptPosition.x = nPosX;
6760 lpItem->ptPosition.y = nPosY;
6761 if (uView == LVS_ICON)
6763 lpItem->ptPosition.y -= ICON_TOP_PADDING;
6764 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6765 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
6767 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
6768 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
6771 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
6772 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
6775 else
6777 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
6778 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
6786 return bResult;
6789 /***
6790 * DESCRIPTION:
6791 * Sets the state of one or many items.
6793 * PARAMETER(S):
6794 * [I] infoPtr : valid pointer to the listview structure
6795 * [I]INT : item index
6796 * [I] LPLVITEM : item or subitem info
6798 * RETURN:
6799 * SUCCESS : TRUE
6800 * FAILURE : FALSE
6802 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6804 BOOL bResult = TRUE;
6805 LVITEMW lvItem;
6807 lvItem.iItem = nItem;
6808 lvItem.iSubItem = 0;
6809 lvItem.mask = LVIF_STATE;
6810 lvItem.state = lpLVItem->state;
6811 lvItem.stateMask = lpLVItem->stateMask;
6812 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6814 if (nItem == -1)
6816 /* apply to all items */
6817 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6818 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6820 else
6821 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6823 return bResult;
6826 /***
6827 * DESCRIPTION:
6828 * Sets the text of an item or subitem.
6830 * PARAMETER(S):
6831 * [I] hwnd : window handle
6832 * [I] nItem : item index
6833 * [I] lpLVItem : item or subitem info
6834 * [I] isW : TRUE if input is Unicode
6836 * RETURN:
6837 * SUCCESS : TRUE
6838 * FAILURE : FALSE
6840 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6842 LVITEMW lvItem;
6844 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6846 lvItem.iItem = nItem;
6847 lvItem.iSubItem = lpLVItem->iSubItem;
6848 lvItem.mask = LVIF_TEXT;
6849 lvItem.pszText = lpLVItem->pszText;
6850 lvItem.cchTextMax = lpLVItem->cchTextMax;
6852 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6854 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6857 /***
6858 * DESCRIPTION:
6859 * Set item index that marks the start of a multiple selection.
6861 * PARAMETER(S):
6862 * [I] infoPtr : valid pointer to the listview structure
6863 * [I] INT : index
6865 * RETURN:
6866 * Index number or -1 if there is no selection mark.
6868 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6870 INT nOldIndex = infoPtr->nSelectionMark;
6872 TRACE("(nIndex=%d)\n", nIndex);
6874 infoPtr->nSelectionMark = nIndex;
6876 return nOldIndex;
6879 /***
6880 * DESCRIPTION:
6881 * Sets the text background color.
6883 * PARAMETER(S):
6884 * [I] infoPtr : valid pointer to the listview structure
6885 * [I] COLORREF : text background color
6887 * RETURN:
6888 * SUCCESS : TRUE
6889 * FAILURE : FALSE
6891 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6893 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6895 if (infoPtr->clrTextBk != clrTextBk)
6897 infoPtr->clrTextBk = clrTextBk;
6898 LISTVIEW_InvalidateList(infoPtr);
6901 return TRUE;
6904 /***
6905 * DESCRIPTION:
6906 * Sets the text foreground color.
6908 * PARAMETER(S):
6909 * [I] infoPtr : valid pointer to the listview structure
6910 * [I] COLORREF : text color
6912 * RETURN:
6913 * SUCCESS : TRUE
6914 * FAILURE : FALSE
6916 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6918 TRACE("(clrText=%lx)\n", clrText);
6920 if (infoPtr->clrText != clrText)
6922 infoPtr->clrText = clrText;
6923 LISTVIEW_InvalidateList(infoPtr);
6926 return TRUE;
6929 /* LISTVIEW_SetToolTips */
6930 /* LISTVIEW_SetUnicodeFormat */
6931 /* LISTVIEW_SetWorkAreas */
6933 /***
6934 * DESCRIPTION:
6935 * Callback internally used by LISTVIEW_SortItems()
6937 * PARAMETER(S):
6938 * [I] LPVOID : first LISTVIEW_ITEM to compare
6939 * [I] LPVOID : second LISTVIEW_ITEM to compare
6940 * [I] LPARAM : HWND of control
6942 * RETURN:
6943 * if first comes before second : negative
6944 * if first comes after second : positive
6945 * if first and second are equivalent : zero
6947 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6949 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6950 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6951 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6953 /* Forward the call to the client defined callback */
6954 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6957 /***
6958 * DESCRIPTION:
6959 * Sorts the listview items.
6961 * PARAMETER(S):
6962 * [I] infoPtr : valid pointer to the listview structure
6963 * [I] WPARAM : application-defined value
6964 * [I] LPARAM : pointer to comparision callback
6966 * RETURN:
6967 * SUCCESS : TRUE
6968 * FAILURE : FALSE
6970 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6972 UINT lStyle = infoPtr->dwStyle;
6973 HDPA hdpaSubItems;
6974 LISTVIEW_ITEM *lpItem;
6975 LPVOID selectionMarkItem;
6976 int i;
6978 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6980 if (lStyle & LVS_OWNERDATA) return FALSE;
6982 if (!infoPtr->hdpaItems) return FALSE;
6984 /* if there are 0 or 1 items, there is no need to sort */
6985 if (infoPtr->nItemCount < 2) return TRUE;
6987 if (infoPtr->nFocusedItem >= 0)
6989 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6990 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6991 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6994 infoPtr->pfnCompare = pfnCompare;
6995 infoPtr->lParamSort = lParamSort;
6996 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6998 /* Adjust selections and indices so that they are the way they should
6999 * be after the sort (otherwise, the list items move around, but
7000 * whatever is at the item's previous original position will be
7001 * selected instead)
7003 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7004 for (i=0; i < infoPtr->nItemCount; i++)
7006 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7007 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7009 if (lpItem->state & LVIS_SELECTED)
7010 LISTVIEW_AddSelectionRange(infoPtr, i, i);
7011 else
7012 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
7013 if (lpItem->state & LVIS_FOCUSED)
7015 infoPtr->nFocusedItem = i;
7016 lpItem->state &= ~LVIS_FOCUSED;
7019 if (selectionMarkItem != NULL)
7020 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7021 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7023 /* align the items */
7024 LISTVIEW_AlignTop(infoPtr);
7026 /* refresh the display */
7027 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
7029 return TRUE;
7032 /***
7033 * DESCRIPTION:
7034 * Updates an items or rearranges the listview control.
7036 * PARAMETER(S):
7037 * [I] infoPtr : valid pointer to the listview structure
7038 * [I] INT : item index
7040 * RETURN:
7041 * SUCCESS : TRUE
7042 * FAILURE : FALSE
7044 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7046 LONG lStyle = infoPtr->dwStyle;
7047 UINT uView = lStyle & LVS_TYPEMASK;
7049 TRACE("(nItem=%d)\n", nItem);
7051 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7053 /* rearrange with default alignment style */
7054 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
7055 LISTVIEW_Arrange(infoPtr, 0);
7056 else
7057 LISTVIEW_InvalidateItem(infoPtr, nItem);
7059 return TRUE;
7063 /***
7064 * DESCRIPTION:
7065 * Creates the listview control.
7067 * PARAMETER(S):
7068 * [I] hwnd : window handle
7069 * [I] lpcs : the create parameters
7071 * RETURN:
7072 * Success: 0
7073 * Failure: -1
7075 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7077 LISTVIEW_INFO *infoPtr;
7078 UINT uView = lpcs->style & LVS_TYPEMASK;
7079 LOGFONTW logFont;
7081 TRACE("(lpcs=%p)\n", lpcs);
7083 /* initialize info pointer */
7084 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7085 if (!infoPtr) return -1;
7087 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7089 infoPtr->hwndSelf = hwnd;
7090 infoPtr->dwStyle = lpcs->style;
7091 /* determine the type of structures to use */
7092 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7093 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7095 /* initialize color information */
7096 infoPtr->clrBk = CLR_NONE;
7097 infoPtr->clrText = comctl32_color.clrWindowText;
7098 infoPtr->clrTextBk = CLR_DEFAULT;
7099 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7101 /* set default values */
7102 infoPtr->nFocusedItem = -1;
7103 infoPtr->nSelectionMark = -1;
7104 infoPtr->nHotItem = -1;
7105 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7106 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7107 infoPtr->nEditLabelItem = -1;
7109 /* get default font (icon title) */
7110 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7111 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7112 infoPtr->hFont = infoPtr->hDefaultFont;
7113 LISTVIEW_SaveTextMetrics(infoPtr);
7115 /* create header */
7116 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7117 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7118 0, 0, 0, 0, hwnd, (HMENU)0,
7119 lpcs->hInstance, NULL);
7121 /* set header unicode format */
7122 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
7124 /* set header font */
7125 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7126 (LPARAM)TRUE);
7128 if (uView == LVS_ICON)
7130 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7131 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7133 else if (uView == LVS_REPORT)
7135 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7137 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7139 else
7141 /* set HDS_HIDDEN flag to hide the header bar */
7142 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7143 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7147 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7148 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7150 else
7152 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7153 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7156 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
7157 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
7159 /* display unsupported listview window styles */
7160 LISTVIEW_UnsupportedStyles(lpcs->style);
7162 /* allocate memory for the data structure */
7163 infoPtr->hdpaItems = DPA_Create(10);
7165 /* allocate memory for the selection ranges */
7166 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7168 /* initialize size of items */
7169 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7170 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7172 /* initialize the hover time to -1(indicating the default system hover time) */
7173 infoPtr->dwHoverTime = -1;
7175 return 0;
7178 /***
7179 * DESCRIPTION:
7180 * Erases the background of the listview control.
7182 * PARAMETER(S):
7183 * [I] infoPtr : valid pointer to the listview structure
7184 * [I] hdc : device context handle
7186 * RETURN:
7187 * SUCCESS : TRUE
7188 * FAILURE : FALSE
7190 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7192 RECT rc;
7194 TRACE("(hdc=%x)\n", hdc);
7196 if (!GetClipBox(hdc, &rc)) return FALSE;
7198 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7202 /***
7203 * DESCRIPTION:
7204 * Helper function for LISTVIEW_[HV]Scroll *only*.
7205 * Performs vertical/horizontal scrolling by a give amount.
7207 * PARAMETER(S):
7208 * [I] infoPtr : valid pointer to the listview structure
7209 * [I] dx : amount of horizontal scroll
7210 * [I] dy : amount of vertical scroll
7212 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7214 /* now we can scroll the list */
7215 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7216 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7217 /* if we have focus, adjust rect */
7218 OffsetRect(&infoPtr->rcFocus, dx, dy);
7219 UpdateWindow(infoPtr->hwndSelf);
7222 /***
7223 * DESCRIPTION:
7224 * Performs vertical scrolling.
7226 * PARAMETER(S):
7227 * [I] infoPtr : valid pointer to the listview structure
7228 * [I] nScrollCode : scroll code
7229 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7230 * [I] hScrollWnd : scrollbar control window handle
7232 * RETURN:
7233 * Zero
7235 * NOTES:
7236 * SB_LINEUP/SB_LINEDOWN:
7237 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7238 * for LVS_REPORT is 1 line
7239 * for LVS_LIST cannot occur
7242 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7243 INT nScrollDiff, HWND hScrollWnd)
7245 UINT uView = LISTVIEW_GetType(infoPtr);
7246 INT nOldScrollPos, nNewScrollPos;
7247 SCROLLINFO scrollInfo;
7248 BOOL is_an_icon;
7250 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7252 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7254 scrollInfo.cbSize = sizeof(SCROLLINFO);
7255 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7257 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7259 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7261 nOldScrollPos = scrollInfo.nPos;
7262 switch (nScrollCode)
7264 case SB_INTERNAL:
7265 break;
7267 case SB_LINEUP:
7268 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7269 break;
7271 case SB_LINEDOWN:
7272 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7273 break;
7275 case SB_PAGEUP:
7276 nScrollDiff = -scrollInfo.nPage;
7277 break;
7279 case SB_PAGEDOWN:
7280 nScrollDiff = scrollInfo.nPage;
7281 break;
7283 case SB_THUMBPOSITION:
7284 case SB_THUMBTRACK:
7285 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7286 break;
7288 default:
7289 nScrollDiff = 0;
7292 /* quit right away if pos isn't changing */
7293 if (nScrollDiff == 0) return 0;
7295 /* calculate new position, and handle overflows */
7296 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7297 if (nScrollDiff > 0) {
7298 if (nNewScrollPos < nOldScrollPos ||
7299 nNewScrollPos > scrollInfo.nMax)
7300 nNewScrollPos = scrollInfo.nMax;
7301 } else {
7302 if (nNewScrollPos > nOldScrollPos ||
7303 nNewScrollPos < scrollInfo.nMin)
7304 nNewScrollPos = scrollInfo.nMin;
7307 /* set the new position, and reread in case it changed */
7308 scrollInfo.fMask = SIF_POS;
7309 scrollInfo.nPos = nNewScrollPos;
7310 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7312 /* carry on only if it really changed */
7313 if (nNewScrollPos == nOldScrollPos) return 0;
7315 /* now adjust to client coordinates */
7316 nScrollDiff = nOldScrollPos - nNewScrollPos;
7317 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7319 /* and scroll the window */
7320 scroll_list(infoPtr, 0, nScrollDiff);
7322 return 0;
7325 /***
7326 * DESCRIPTION:
7327 * Performs horizontal scrolling.
7329 * PARAMETER(S):
7330 * [I] infoPtr : valid pointer to the listview structure
7331 * [I] nScrollCode : scroll code
7332 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7333 * [I] hScrollWnd : scrollbar control window handle
7335 * RETURN:
7336 * Zero
7338 * NOTES:
7339 * SB_LINELEFT/SB_LINERIGHT:
7340 * for LVS_ICON, LVS_SMALLICON 1 pixel
7341 * for LVS_REPORT is 1 pixel
7342 * for LVS_LIST is 1 column --> which is a 1 because the
7343 * scroll is based on columns not pixels
7346 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7347 INT nScrollDiff, HWND hScrollWnd)
7349 UINT uView = LISTVIEW_GetType(infoPtr);
7350 INT nOldScrollPos, nNewScrollPos;
7351 SCROLLINFO scrollInfo;
7353 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7355 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7357 scrollInfo.cbSize = sizeof(SCROLLINFO);
7358 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7360 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7362 nOldScrollPos = scrollInfo.nPos;
7364 switch (nScrollCode)
7366 case SB_INTERNAL:
7367 break;
7369 case SB_LINELEFT:
7370 nScrollDiff = -1;
7371 break;
7373 case SB_LINERIGHT:
7374 nScrollDiff = 1;
7375 break;
7377 case SB_PAGELEFT:
7378 nScrollDiff = -scrollInfo.nPage;
7379 break;
7381 case SB_PAGERIGHT:
7382 nScrollDiff = scrollInfo.nPage;
7383 break;
7385 case SB_THUMBPOSITION:
7386 case SB_THUMBTRACK:
7387 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7388 break;
7390 default:
7391 nScrollDiff = 0;
7394 /* quit right away if pos isn't changing */
7395 if (nScrollDiff == 0) return 0;
7397 /* calculate new position, and handle overflows */
7398 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7399 if (nScrollDiff > 0) {
7400 if (nNewScrollPos < nOldScrollPos ||
7401 nNewScrollPos > scrollInfo.nMax)
7402 nNewScrollPos = scrollInfo.nMax;
7403 } else {
7404 if (nNewScrollPos > nOldScrollPos ||
7405 nNewScrollPos < scrollInfo.nMin)
7406 nNewScrollPos = scrollInfo.nMin;
7409 /* set the new position, and reread in case it changed */
7410 scrollInfo.fMask = SIF_POS;
7411 scrollInfo.nPos = nNewScrollPos;
7412 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7414 /* carry on only if it really changed */
7415 if (nNewScrollPos == nOldScrollPos) return 0;
7417 if(uView == LVS_REPORT)
7418 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7420 /* now adjust to client coordinates */
7421 nScrollDiff = nOldScrollPos - nNewScrollPos;
7422 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7424 /* and scroll the window */
7425 scroll_list(infoPtr, nScrollDiff, 0);
7427 return 0;
7430 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7432 UINT uView = LISTVIEW_GetType(infoPtr);
7433 INT gcWheelDelta = 0;
7434 UINT pulScrollLines = 3;
7435 SCROLLINFO scrollInfo;
7437 TRACE("(wheelDelta=%d)\n", wheelDelta);
7439 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7440 gcWheelDelta -= wheelDelta;
7442 scrollInfo.cbSize = sizeof(SCROLLINFO);
7443 scrollInfo.fMask = SIF_POS;
7445 switch(uView)
7447 case LVS_ICON:
7448 case LVS_SMALLICON:
7450 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7451 * should be fixed in the future.
7453 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7454 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7455 scrollInfo.nPos + (gcWheelDelta < 0) ?
7456 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7457 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7458 break;
7460 case LVS_REPORT:
7461 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7463 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7465 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7466 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7467 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7470 break;
7472 case LVS_LIST:
7473 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7474 break;
7476 return 0;
7479 /***
7480 * DESCRIPTION:
7481 * ???
7483 * PARAMETER(S):
7484 * [I] infoPtr : valid pointer to the listview structure
7485 * [I] INT : virtual key
7486 * [I] LONG : key data
7488 * RETURN:
7489 * Zero
7491 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7493 UINT uView = LISTVIEW_GetType(infoPtr);
7494 INT nItem = -1;
7495 NMLVKEYDOWN nmKeyDown;
7497 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7499 /* send LVN_KEYDOWN notification */
7500 nmKeyDown.wVKey = nVirtualKey;
7501 nmKeyDown.flags = 0;
7502 notify(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7504 switch (nVirtualKey)
7506 case VK_RETURN:
7507 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7509 notify_return(infoPtr);
7510 notify_itemactivate(infoPtr);
7512 break;
7514 case VK_HOME:
7515 if (infoPtr->nItemCount > 0)
7516 nItem = 0;
7517 break;
7519 case VK_END:
7520 if (infoPtr->nItemCount > 0)
7521 nItem = infoPtr->nItemCount - 1;
7522 break;
7524 case VK_LEFT:
7525 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7526 break;
7528 case VK_UP:
7529 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7530 break;
7532 case VK_RIGHT:
7533 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7534 break;
7536 case VK_DOWN:
7537 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7538 break;
7540 case VK_PRIOR:
7541 if (uView == LVS_REPORT)
7542 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7543 else
7544 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7545 * LISTVIEW_GetCountPerRow(infoPtr);
7546 if(nItem < 0) nItem = 0;
7547 break;
7549 case VK_NEXT:
7550 if (uView == LVS_REPORT)
7551 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7552 else
7553 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7554 * LISTVIEW_GetCountPerRow(infoPtr);
7555 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7556 break;
7559 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7560 LISTVIEW_KeySelection(infoPtr, nItem);
7562 return 0;
7565 /***
7566 * DESCRIPTION:
7567 * Kills the focus.
7569 * PARAMETER(S):
7570 * [I] infoPtr : valid pointer to the listview structure
7572 * RETURN:
7573 * Zero
7575 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7577 TRACE("()\n");
7579 /* if we did not have the focus, there's nothing to do */
7580 if (!infoPtr->bFocus) return 0;
7582 /* send NM_KILLFOCUS notification */
7583 notify_killfocus(infoPtr);
7585 /* if we have a focus rectagle, get rid of it */
7586 LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, FALSE);
7588 /* set window focus flag */
7589 infoPtr->bFocus = FALSE;
7591 /* invalidate the selected items before reseting focus flag */
7592 LISTVIEW_InvalidateSelectedItems(infoPtr);
7594 return 0;
7597 /***
7598 * DESCRIPTION:
7599 * Processes double click messages (left mouse button).
7601 * PARAMETER(S):
7602 * [I] infoPtr : valid pointer to the listview structure
7603 * [I] wKey : key flag
7604 * [I] pts : mouse coordinate
7606 * RETURN:
7607 * Zero
7609 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7611 LVHITTESTINFO htInfo;
7612 NMLISTVIEW nmlv;
7614 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7616 htInfo.pt.x = pts.x;
7617 htInfo.pt.y = pts.y;
7619 /* send NM_DBLCLK notification */
7620 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7621 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE);
7622 nmlv.iItem = htInfo.iItem;
7623 nmlv.iSubItem = htInfo.iSubItem;
7624 nmlv.ptAction = htInfo.pt;
7625 notify_listview(infoPtr, NM_DBLCLK, &nmlv);
7627 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7628 if(nmlv.iItem != -1)
7629 notify_itemactivate(infoPtr);
7631 return 0;
7634 /***
7635 * DESCRIPTION:
7636 * Processes mouse down messages (left mouse button).
7638 * PARAMETER(S):
7639 * [I] infoPtr : valid pointer to the listview structure
7640 * [I] wKey : key flag
7641 * [I] pts : mouse coordinate
7643 * RETURN:
7644 * Zero
7646 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7648 LONG lStyle = infoPtr->dwStyle;
7649 static BOOL bGroupSelect = TRUE;
7650 POINT pt = { pts.x, pts.y };
7651 INT nItem;
7653 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7655 /* FIXME: NM_CLICK */
7657 /* send NM_RELEASEDCAPTURE notification */
7658 notify_releasedcapture(infoPtr);
7660 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7662 /* set left button down flag */
7663 infoPtr->bLButtonDown = TRUE;
7665 nItem = LISTVIEW_GetItemAtPt(infoPtr, pt);
7666 TRACE("nItem=%d\n", nItem);
7667 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7669 if (lStyle & LVS_SINGLESEL)
7671 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7672 && infoPtr->nEditLabelItem == -1)
7673 infoPtr->nEditLabelItem = nItem;
7674 else
7675 LISTVIEW_SetSelection(infoPtr, nItem);
7677 else
7679 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7681 if (bGroupSelect)
7682 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7683 else
7685 LVITEMW item;
7687 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7688 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7690 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7691 infoPtr->nSelectionMark = nItem;
7694 else if (wKey & MK_CONTROL)
7696 LVITEMW item;
7698 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7700 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7701 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7702 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7703 infoPtr->nSelectionMark = nItem;
7705 else if (wKey & MK_SHIFT)
7707 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7709 else
7711 BOOL was_selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
7713 /* set selection (clears other pre-existing selections) */
7714 LISTVIEW_SetSelection(infoPtr, nItem);
7716 if (was_selected && infoPtr->nEditLabelItem == -1)
7717 infoPtr->nEditLabelItem = nItem;
7721 else
7723 /* remove all selections */
7724 LISTVIEW_RemoveAllSelections(infoPtr);
7727 return 0;
7730 /***
7731 * DESCRIPTION:
7732 * Processes mouse up messages (left mouse button).
7734 * PARAMETER(S):
7735 * [I] infoPtr : valid pointer to the listview structure
7736 * [I] wKey : key flag
7737 * [I] pts : mouse coordinate
7739 * RETURN:
7740 * Zero
7742 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7744 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7746 if (infoPtr->bLButtonDown)
7748 LVHITTESTINFO lvHitTestInfo;
7749 NMLISTVIEW nmlv;
7751 lvHitTestInfo.pt.x = pts.x;
7752 lvHitTestInfo.pt.y = pts.y;
7754 /* send NM_CLICK notification */
7755 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7756 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE) != -1)
7758 nmlv.iItem = lvHitTestInfo.iItem;
7759 nmlv.iSubItem = lvHitTestInfo.iSubItem;
7761 else
7763 nmlv.iItem = -1;
7764 nmlv.iSubItem = 0;
7766 nmlv.ptAction.x = pts.x;
7767 nmlv.ptAction.y = pts.y;
7768 notify_listview(infoPtr, NM_CLICK, &nmlv);
7770 /* set left button flag */
7771 infoPtr->bLButtonDown = FALSE;
7773 if(infoPtr->nEditLabelItem != -1)
7775 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL) {
7776 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7778 infoPtr->nEditLabelItem = -1;
7782 return 0;
7785 /***
7786 * DESCRIPTION:
7787 * Destroys the listview control (called after WM_DESTROY).
7789 * PARAMETER(S):
7790 * [I] infoPtr : valid pointer to the listview structure
7792 * RETURN:
7793 * Zero
7795 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7797 LONG lStyle = infoPtr->dwStyle;
7799 TRACE("()\n");
7801 /* delete all items */
7802 LISTVIEW_DeleteAllItems(infoPtr);
7804 /* destroy data structure */
7805 DPA_Destroy(infoPtr->hdpaItems);
7806 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7808 /* destroy image lists */
7809 if (!(lStyle & LVS_SHAREIMAGELISTS))
7811 /* FIXME: If the caller does a ImageList_Destroy and then we
7812 * do this code the area will be freed twice. Currently
7813 * this generates an "err:heap:HEAP_ValidateInUseArena
7814 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7815 * has PREV_FREE flag" sometimes.
7817 * We will leak the memory till we figure out how to fix
7819 if (infoPtr->himlNormal)
7820 ImageList_Destroy(infoPtr->himlNormal);
7821 if (infoPtr->himlSmall)
7822 ImageList_Destroy(infoPtr->himlSmall);
7823 if (infoPtr->himlState)
7824 ImageList_Destroy(infoPtr->himlState);
7827 /* destroy font, bkgnd brush */
7828 infoPtr->hFont = 0;
7829 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7830 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7832 /* free listview info pointer*/
7833 COMCTL32_Free(infoPtr);
7835 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7836 return 0;
7839 /***
7840 * DESCRIPTION:
7841 * Handles notifications from children.
7843 * PARAMETER(S):
7844 * [I] infoPtr : valid pointer to the listview structure
7845 * [I] INT : control identifier
7846 * [I] LPNMHDR : notification information
7848 * RETURN:
7849 * Zero
7851 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7853 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7855 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7857 /* handle notification from header control */
7858 if (lpnmh->code == HDN_ENDTRACKW)
7860 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7861 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7863 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7865 /* Handle sorting by Header Column */
7866 NMLISTVIEW nmlv;
7868 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7869 nmlv.iItem = -1;
7870 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7871 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7873 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7875 /* Idealy this should be done in HDN_ENDTRACKA
7876 * but since SetItemBounds in Header.c is called after
7877 * the notification is sent, it is neccessary to handle the
7878 * update of the scroll bar here (Header.c works fine as it is,
7879 * no need to disturb it)
7881 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7882 LISTVIEW_UpdateScroll(infoPtr);
7883 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7888 return 0;
7891 /***
7892 * DESCRIPTION:
7893 * Determines the type of structure to use.
7895 * PARAMETER(S):
7896 * [I] infoPtr : valid pointer to the listview structureof the sender
7897 * [I] HWND : listview window handle
7898 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7900 * RETURN:
7901 * Zero
7903 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7905 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7907 if (nCommand == NF_REQUERY)
7908 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7909 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7910 return 0;
7913 /***
7914 * DESCRIPTION:
7915 * Paints/Repaints the listview control.
7917 * PARAMETER(S):
7918 * [I] infoPtr : valid pointer to the listview structure
7919 * [I] HDC : device context handle
7921 * RETURN:
7922 * Zero
7924 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7926 TRACE("(hdc=%x)\n", hdc);
7928 if (hdc)
7929 LISTVIEW_Refresh(infoPtr, hdc);
7930 else
7932 PAINTSTRUCT ps;
7934 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7935 if (!hdc) return 1;
7936 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7937 LISTVIEW_Refresh(infoPtr, hdc);
7938 EndPaint(infoPtr->hwndSelf, &ps);
7941 return 0;
7944 /***
7945 * DESCRIPTION:
7946 * Processes double click messages (right mouse button).
7948 * PARAMETER(S):
7949 * [I] infoPtr : valid pointer to the listview structure
7950 * [I] wKey : key flag
7951 * [I] pts : mouse coordinate
7953 * RETURN:
7954 * Zero
7956 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7958 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7960 /* send NM_RELEASEDCAPTURE notification */
7961 notify_releasedcapture(infoPtr);
7963 /* send NM_RDBLCLK notification */
7964 notify_rdblclk(infoPtr);
7966 return 0;
7969 /***
7970 * DESCRIPTION:
7971 * Processes mouse down messages (right mouse button).
7973 * PARAMETER(S):
7974 * [I] infoPtr : valid pointer to the listview structure
7975 * [I] wKey : key flag
7976 * [I] pts : mouse coordinate
7978 * RETURN:
7979 * Zero
7981 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7983 LVHITTESTINFO lvHitTestInfo;
7984 NMLISTVIEW nmlv;
7985 INT nItem;
7987 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7989 /* FIXME: NM_CLICK */
7991 /* send NM_RELEASEDCAPTURE notification */
7992 notify_releasedcapture(infoPtr);
7994 /* make sure the listview control window has the focus */
7995 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7997 /* set right button down flag */
7998 infoPtr->bRButtonDown = TRUE;
8000 /* determine the index of the selected item */
8001 lvHitTestInfo.pt.x = pts.x;
8002 lvHitTestInfo.pt.y = pts.y;
8003 nItem = LISTVIEW_GetItemAtPt(infoPtr, lvHitTestInfo.pt);
8005 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8007 LISTVIEW_SetItemFocus(infoPtr,nItem);
8008 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8009 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8010 LISTVIEW_SetSelection(infoPtr, nItem);
8012 else
8014 LISTVIEW_RemoveAllSelections(infoPtr);
8018 /* Send NM_RClICK notification */
8019 ZeroMemory(&nmlv, sizeof(nmlv));
8020 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE);
8021 nmlv.iItem = lvHitTestInfo.iItem;
8022 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8023 nmlv.ptAction = lvHitTestInfo.pt;
8024 notify_listview(infoPtr, NM_RCLICK, &nmlv);
8026 return 0;
8029 /***
8030 * DESCRIPTION:
8031 * Processes mouse up messages (right mouse button).
8033 * PARAMETER(S):
8034 * [I] infoPtr : valid pointer to the listview structure
8035 * [I] wKey : key flag
8036 * [I] pts : mouse coordinate
8038 * RETURN:
8039 * Zero
8041 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8043 POINT pt = { pts.x, pts.y };
8045 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8047 if (!infoPtr->bRButtonDown) return 0;
8049 /* set button flag */
8050 infoPtr->bRButtonDown = FALSE;
8052 /* Change to screen coordinate for WM_CONTEXTMENU */
8053 ClientToScreen(infoPtr->hwndSelf, &pt);
8055 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8056 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8057 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8059 return 0;
8063 /***
8064 * DESCRIPTION:
8065 * Sets the cursor.
8067 * PARAMETER(S):
8068 * [I] infoPtr : valid pointer to the listview structure
8069 * [I] hwnd : window handle of window containing the cursor
8070 * [I] nHittest : hit-test code
8071 * [I] wMouseMsg : ideintifier of the mouse message
8073 * RETURN:
8074 * TRUE if cursor is set
8075 * FALSE otherwise
8077 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8079 POINT pt;
8081 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8083 if(!infoPtr->hHotCursor) return FALSE;
8085 GetCursorPos(&pt);
8086 if (LISTVIEW_GetItemAtPt(infoPtr, pt) < 0) return FALSE;
8088 SetCursor(infoPtr->hHotCursor);
8090 return TRUE;
8093 /***
8094 * DESCRIPTION:
8095 * Sets the focus.
8097 * PARAMETER(S):
8098 * [I] infoPtr : valid pointer to the listview structure
8099 * [I] infoPtr : handle of previously focused window
8101 * RETURN:
8102 * Zero
8104 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8106 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
8108 /* if we have the focus already, there's nothing to do */
8109 if (infoPtr->bFocus) return 0;
8111 /* send NM_SETFOCUS notification */
8112 notify_setfocus(infoPtr);
8114 /* set window focus flag */
8115 infoPtr->bFocus = TRUE;
8117 /* put the focus rect back on */
8118 LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, TRUE);
8120 /* redraw all visible selected items */
8121 LISTVIEW_InvalidateSelectedItems(infoPtr);
8123 return 0;
8126 /***
8127 * DESCRIPTION:
8128 * Sets the font.
8130 * PARAMETER(S):
8131 * [I] infoPtr : valid pointer to the listview structure
8132 * [I] HFONT : font handle
8133 * [I] WORD : redraw flag
8135 * RETURN:
8136 * Zero
8138 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8140 HFONT oldFont = infoPtr->hFont;
8142 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
8144 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8145 if (infoPtr->hFont == oldFont) return 0;
8147 LISTVIEW_SaveTextMetrics(infoPtr);
8149 if (LISTVIEW_GetType(infoPtr) == LVS_REPORT)
8150 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8152 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8154 return 0;
8157 /***
8158 * DESCRIPTION:
8159 * Message handling for WM_SETREDRAW.
8160 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8162 * PARAMETER(S):
8163 * [I] infoPtr : valid pointer to the listview structure
8164 * [I] bRedraw: state of redraw flag
8166 * RETURN:
8167 * DefWinProc return value
8169 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8171 /* FIXME: this is bogus */
8172 LRESULT lResult = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, bRedraw, 0);
8173 if(bRedraw)
8174 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8175 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8176 return lResult;
8179 /***
8180 * DESCRIPTION:
8181 * Resizes the listview control. This function processes WM_SIZE
8182 * messages. At this time, the width and height are not used.
8184 * PARAMETER(S):
8185 * [I] infoPtr : valid pointer to the listview structure
8186 * [I] WORD : new width
8187 * [I] WORD : new height
8189 * RETURN:
8190 * Zero
8192 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8194 LONG lStyle = infoPtr->dwStyle;
8195 UINT uView = lStyle & LVS_TYPEMASK;
8197 TRACE("(width=%d, height=%d)\n", Width, Height);
8199 if (LISTVIEW_UpdateSize(infoPtr))
8201 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8203 if (lStyle & LVS_ALIGNLEFT)
8204 LISTVIEW_AlignLeft(infoPtr);
8205 else
8206 LISTVIEW_AlignTop(infoPtr);
8209 LISTVIEW_UpdateScroll(infoPtr);
8211 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8214 return 0;
8217 /***
8218 * DESCRIPTION:
8219 * Sets the size information.
8221 * PARAMETER(S):
8222 * [I] infoPtr : valid pointer to the listview structure
8224 * RETURN:
8225 * Zero if no size change
8226 * 1 of size changed
8228 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8230 LONG lStyle = infoPtr->dwStyle;
8231 UINT uView = lStyle & LVS_TYPEMASK;
8232 RECT rcList;
8233 RECT rcOld;
8235 GetClientRect(infoPtr->hwndSelf, &rcList);
8236 CopyRect(&rcOld,&(infoPtr->rcList));
8237 infoPtr->rcList.left = 0;
8238 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8239 infoPtr->rcList.top = 0;
8240 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8242 if (uView == LVS_LIST)
8244 /* Apparently the "LIST" style is supposed to have the same
8245 * number of items in a column even if there is no scroll bar.
8246 * Since if a scroll bar already exists then the bottom is already
8247 * reduced, only reduce if the scroll bar does not currently exist.
8248 * The "2" is there to mimic the native control. I think it may be
8249 * related to either padding or edges. (GLA 7/2002)
8251 if (!(lStyle & WS_HSCROLL))
8253 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8254 if (infoPtr->rcList.bottom > nHScrollHeight)
8255 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8257 else
8259 if (infoPtr->rcList.bottom > 2)
8260 infoPtr->rcList.bottom -= 2;
8263 else if (uView == LVS_REPORT)
8265 HDLAYOUT hl;
8266 WINDOWPOS wp;
8268 hl.prc = &rcList;
8269 hl.pwpos = &wp;
8270 Header_Layout(infoPtr->hwndHeader, &hl);
8272 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8274 if (!(LVS_NOCOLUMNHEADER & lStyle))
8275 infoPtr->rcList.top = max(wp.cy, 0);
8277 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8280 /***
8281 * DESCRIPTION:
8282 * Processes WM_STYLECHANGED messages.
8284 * PARAMETER(S):
8285 * [I] infoPtr : valid pointer to the listview structure
8286 * [I] WPARAM : window style type (normal or extended)
8287 * [I] LPSTYLESTRUCT : window style information
8289 * RETURN:
8290 * Zero
8292 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8293 LPSTYLESTRUCT lpss)
8295 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8296 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8297 RECT rcList = infoPtr->rcList;
8299 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8300 wStyleType, lpss->styleOld, lpss->styleNew);
8302 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8304 if (wStyleType == GWL_STYLE)
8306 infoPtr->dwStyle = lpss->styleNew;
8308 if (uOldView == LVS_REPORT)
8309 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8311 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8312 ((lpss->styleNew & WS_HSCROLL) == 0))
8313 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8315 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8316 ((lpss->styleNew & WS_VSCROLL) == 0))
8317 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8319 /* If switching modes, then start with no scroll bars and then
8320 * decide.
8322 if (uNewView != uOldView)
8323 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8325 if (uNewView == LVS_ICON)
8327 INT oldcx, oldcy;
8329 /* First readjust the iconSize and if necessary the iconSpacing */
8330 oldcx = infoPtr->iconSize.cx;
8331 oldcy = infoPtr->iconSize.cy;
8332 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8333 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8334 if (infoPtr->himlNormal != NULL)
8336 INT cx, cy;
8337 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8338 infoPtr->iconSize.cx = cx;
8339 infoPtr->iconSize.cy = cy;
8341 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8343 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8344 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8345 LISTVIEW_SetIconSpacing(infoPtr,0);
8348 /* Now update the full item width and height */
8349 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8350 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8351 if (lpss->styleNew & LVS_ALIGNLEFT)
8352 LISTVIEW_AlignLeft(infoPtr);
8353 else
8354 LISTVIEW_AlignTop(infoPtr);
8356 else if (uNewView == LVS_REPORT)
8358 HDLAYOUT hl;
8359 WINDOWPOS wp;
8361 hl.prc = &rcList;
8362 hl.pwpos = &wp;
8363 Header_Layout(infoPtr->hwndHeader, &hl);
8364 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8365 wp.flags);
8366 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8367 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8369 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8370 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8371 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8372 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8374 else if (uNewView == LVS_LIST)
8376 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8377 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8378 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8379 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8381 else
8383 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8384 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8385 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8386 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8387 if (lpss->styleNew & LVS_ALIGNLEFT)
8388 LISTVIEW_AlignLeft(infoPtr);
8389 else
8390 LISTVIEW_AlignTop(infoPtr);
8393 /* update the size of the client area */
8394 LISTVIEW_UpdateSize(infoPtr);
8396 /* add scrollbars if needed */
8397 LISTVIEW_UpdateScroll(infoPtr);
8399 /* invalidate client area + erase background */
8400 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8402 /* print the list of unsupported window styles */
8403 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8406 /* If they change the view and we have an active edit control
8407 we will need to kill the control since the redraw will
8408 misplace the edit control.
8410 if (infoPtr->bEditing &&
8411 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8412 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8414 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8417 return 0;
8420 /***
8421 * DESCRIPTION:
8422 * Window procedure of the listview control.
8425 static LRESULT WINAPI
8426 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8428 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8430 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8432 if (!infoPtr && (uMsg != WM_CREATE))
8433 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8435 if (infoPtr)
8437 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8440 switch (uMsg)
8442 case LVM_APPROXIMATEVIEWRECT:
8443 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8444 LOWORD(lParam), HIWORD(lParam));
8445 case LVM_ARRANGE:
8446 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8448 /* case LVN_CANCELEDITLABEL */
8450 /* case LVM_CREATEDRAGIMAGE: */
8452 case LVM_DELETEALLITEMS:
8453 return LISTVIEW_DeleteAllItems(infoPtr);
8455 case LVM_DELETECOLUMN:
8456 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8458 case LVM_DELETEITEM:
8459 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8461 case LVM_EDITLABELW:
8462 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8464 case LVM_EDITLABELA:
8465 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8467 /* case LVN_ENABLEGROUPVIEW: */
8469 case LVM_ENSUREVISIBLE:
8470 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8472 case LVM_FINDITEMW:
8473 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8475 case LVM_FINDITEMA:
8476 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8478 case LVM_GETBKCOLOR:
8479 return infoPtr->clrBk;
8481 /* case LVM_GETBKIMAGE: */
8483 case LVM_GETCALLBACKMASK:
8484 return infoPtr->uCallbackMask;
8486 case LVM_GETCOLUMNA:
8487 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8489 case LVM_GETCOLUMNW:
8490 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8492 case LVM_GETCOLUMNORDERARRAY:
8493 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8495 case LVM_GETCOLUMNWIDTH:
8496 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8498 case LVM_GETCOUNTPERPAGE:
8499 return LISTVIEW_GetCountPerPage(infoPtr);
8501 case LVM_GETEDITCONTROL:
8502 return (LRESULT)infoPtr->hwndEdit;
8504 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8505 return infoPtr->dwLvExStyle;
8507 case LVM_GETHEADER:
8508 return (LRESULT)infoPtr->hwndHeader;
8510 case LVM_GETHOTCURSOR:
8511 return infoPtr->hHotCursor;
8513 case LVM_GETHOTITEM:
8514 return infoPtr->nHotItem;
8516 case LVM_GETHOVERTIME:
8517 return infoPtr->dwHoverTime;
8519 case LVM_GETIMAGELIST:
8520 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8522 /* case LVN_GETINSERTMARK: */
8524 /* case LVN_GETINSERTMARKCOLOR: */
8526 /* case LVN_GETINSERTMARKRECT: */
8528 case LVM_GETISEARCHSTRINGA:
8529 case LVM_GETISEARCHSTRINGW:
8530 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8531 return FALSE;
8533 case LVM_GETITEMA:
8534 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8536 case LVM_GETITEMW:
8537 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8539 case LVM_GETITEMCOUNT:
8540 return infoPtr->nItemCount;
8542 case LVM_GETITEMPOSITION:
8543 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8545 case LVM_GETITEMRECT:
8546 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8548 case LVM_GETITEMSPACING:
8549 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8551 case LVM_GETITEMSTATE:
8552 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8554 case LVM_GETITEMTEXTA:
8555 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8557 case LVM_GETITEMTEXTW:
8558 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8560 case LVM_GETNEXTITEM:
8561 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8563 case LVM_GETNUMBEROFWORKAREAS:
8564 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8565 return 1;
8567 case LVM_GETORIGIN:
8568 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8570 /* case LVN_GETOUTLINECOLOR: */
8572 /* case LVM_GETSELECTEDCOLUMN: */
8574 case LVM_GETSELECTEDCOUNT:
8575 return LISTVIEW_GetSelectedCount(infoPtr);
8577 case LVM_GETSELECTIONMARK:
8578 return infoPtr->nSelectionMark;
8580 case LVM_GETSTRINGWIDTHA:
8581 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8583 case LVM_GETSTRINGWIDTHW:
8584 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8586 case LVM_GETSUBITEMRECT:
8587 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8589 case LVM_GETTEXTBKCOLOR:
8590 return infoPtr->clrTextBk;
8592 case LVM_GETTEXTCOLOR:
8593 return infoPtr->clrText;
8595 /* case LVN_GETTILEINFO: */
8597 /* case LVN_GETTILEVIEWINFO: */
8599 case LVM_GETTOOLTIPS:
8600 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8601 return FALSE;
8603 case LVM_GETTOPINDEX:
8604 return LISTVIEW_GetTopIndex(infoPtr);
8606 /*case LVM_GETUNICODEFORMAT:
8607 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8608 return FALSE;*/
8610 case LVM_GETVIEWRECT:
8611 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8613 case LVM_GETWORKAREAS:
8614 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8615 return FALSE;
8617 /* case LVN_HASGROUP: */
8619 case LVM_HITTEST:
8620 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE);
8622 case LVM_INSERTCOLUMNA:
8623 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8625 case LVM_INSERTCOLUMNW:
8626 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8628 /* case LVN_INSERTGROUP: */
8630 /* case LVN_INSERTGROUPSORTED: */
8632 case LVM_INSERTITEMA:
8633 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8635 case LVM_INSERTITEMW:
8636 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8638 /* case LVN_INSERTMARKHITTEST: */
8640 /* case LVN_ISGROUPVIEWENABLED: */
8642 /* case LVN_MAPIDTOINDEX: */
8644 /* case LVN_INEDXTOID: */
8646 /* case LVN_MOVEGROUP: */
8648 /* case LVN_MOVEITEMTOGROUP: */
8650 case LVM_REDRAWITEMS:
8651 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8653 /* case LVN_REMOVEALLGROUPS: */
8655 /* case LVN_REMOVEGROUP: */
8657 case LVM_SCROLL:
8658 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8660 case LVM_SETBKCOLOR:
8661 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8663 /* case LVM_SETBKIMAGE: */
8665 case LVM_SETCALLBACKMASK:
8666 infoPtr->uCallbackMask = (UINT)wParam;
8667 return TRUE;
8669 case LVM_SETCOLUMNA:
8670 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8672 case LVM_SETCOLUMNW:
8673 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8675 case LVM_SETCOLUMNORDERARRAY:
8676 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8678 case LVM_SETCOLUMNWIDTH:
8679 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8681 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8682 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8684 /* case LVN_SETGROUPINFO: */
8686 /* case LVN_SETGROUPMETRICS: */
8688 case LVM_SETHOTCURSOR:
8689 return LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8691 case LVM_SETHOTITEM:
8692 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8694 case LVM_SETHOVERTIME:
8695 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8697 case LVM_SETICONSPACING:
8698 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8700 case LVM_SETIMAGELIST:
8701 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8703 /* case LVN_SETINFOTIP: */
8705 /* case LVN_SETINSERTMARK: */
8707 /* case LVN_SETINSERTMARKCOLOR: */
8709 case LVM_SETITEMA:
8710 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8712 case LVM_SETITEMW:
8713 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8715 case LVM_SETITEMCOUNT:
8716 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8718 case LVM_SETITEMPOSITION:
8719 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (INT)LOWORD(lParam),
8720 (INT)HIWORD(lParam));
8722 case LVM_SETITEMPOSITION32:
8723 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, ((POINT*)lParam)->x,
8724 ((POINT*)lParam)->y);
8726 case LVM_SETITEMSTATE:
8727 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8729 case LVM_SETITEMTEXTA:
8730 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8732 case LVM_SETITEMTEXTW:
8733 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8735 /* case LVN_SETOUTLINECOLOR: */
8737 /* case LVN_SETSELECTEDCOLUMN: */
8739 case LVM_SETSELECTIONMARK:
8740 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8742 case LVM_SETTEXTBKCOLOR:
8743 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8745 case LVM_SETTEXTCOLOR:
8746 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8748 /* case LVN_SETTILEINFO: */
8750 /* case LVN_SETTILEVIEWINFO: */
8752 /* case LVN_SETTILEWIDTH: */
8754 /* case LVM_SETTOOLTIPS: */
8756 /* case LVM_SETUNICODEFORMAT: */
8758 /* case LVN_SETVIEW: */
8760 /* case LVM_SETWORKAREAS: */
8762 /* case LVN_SORTGROUPS: */
8764 case LVM_SORTITEMS:
8765 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8767 case LVM_SUBITEMHITTEST:
8768 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE);
8770 case LVM_UPDATE:
8771 return LISTVIEW_Update(infoPtr, (INT)wParam);
8773 case WM_CHAR:
8774 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8776 case WM_COMMAND:
8777 return LISTVIEW_Command(infoPtr, wParam, lParam);
8779 case WM_CREATE:
8780 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8782 case WM_ERASEBKGND:
8783 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8785 case WM_GETDLGCODE:
8786 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8788 case WM_GETFONT:
8789 return infoPtr->hFont;
8791 case WM_HSCROLL:
8792 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8794 case WM_KEYDOWN:
8795 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8797 case WM_KILLFOCUS:
8798 return LISTVIEW_KillFocus(infoPtr);
8800 case WM_LBUTTONDBLCLK:
8801 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8803 case WM_LBUTTONDOWN:
8804 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8806 case WM_LBUTTONUP:
8807 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8809 case WM_MOUSEMOVE:
8810 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8812 case WM_MOUSEHOVER:
8813 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8815 case WM_NCDESTROY:
8816 return LISTVIEW_NCDestroy(infoPtr);
8818 case WM_NOTIFY:
8819 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8821 case WM_NOTIFYFORMAT:
8822 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8824 case WM_PAINT:
8825 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8827 case WM_RBUTTONDBLCLK:
8828 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8830 case WM_RBUTTONDOWN:
8831 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8833 case WM_RBUTTONUP:
8834 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8836 case WM_SETCURSOR:
8837 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8838 return TRUE;
8839 goto fwd_msg;
8841 case WM_SETFOCUS:
8842 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8844 case WM_SETFONT:
8845 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8847 case WM_SETREDRAW:
8848 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8850 case WM_SIZE:
8851 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8853 case WM_STYLECHANGED:
8854 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8856 case WM_SYSCOLORCHANGE:
8857 COMCTL32_RefreshSysColors();
8858 return 0;
8860 /* case WM_TIMER: */
8862 case WM_VSCROLL:
8863 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8865 case WM_MOUSEWHEEL:
8866 if (wParam & (MK_SHIFT | MK_CONTROL))
8867 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8868 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8870 case WM_WINDOWPOSCHANGED:
8871 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8872 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8873 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8874 LISTVIEW_UpdateSize(infoPtr);
8875 LISTVIEW_UpdateScroll(infoPtr);
8877 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8879 /* case WM_WININICHANGE: */
8881 default:
8882 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8883 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8885 fwd_msg:
8886 /* call default window procedure */
8887 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8890 return 0;
8893 /***
8894 * DESCRIPTION:
8895 * Registers the window class.
8897 * PARAMETER(S):
8898 * None
8900 * RETURN:
8901 * None
8903 void LISTVIEW_Register(void)
8905 WNDCLASSW wndClass;
8907 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8908 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8909 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8910 wndClass.cbClsExtra = 0;
8911 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8912 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8913 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8914 wndClass.lpszClassName = WC_LISTVIEWW;
8915 RegisterClassW(&wndClass);
8918 /***
8919 * DESCRIPTION:
8920 * Unregisters the window class.
8922 * PARAMETER(S):
8923 * None
8925 * RETURN:
8926 * None
8928 void LISTVIEW_Unregister(void)
8930 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8933 /***
8934 * DESCRIPTION:
8935 * Handle any WM_COMMAND messages
8937 * PARAMETER(S):
8939 * RETURN:
8941 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8943 switch (HIWORD(wParam))
8945 case EN_UPDATE:
8948 * Adjust the edit window size
8950 WCHAR buffer[1024];
8951 HDC hdc = GetDC(infoPtr->hwndEdit);
8952 HFONT hFont, hOldFont = 0;
8953 RECT rect;
8954 SIZE sz;
8955 int len;
8957 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8958 GetWindowRect(infoPtr->hwndEdit, &rect);
8960 /* Select font to get the right dimension of the string */
8961 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8962 if(hFont != 0)
8964 hOldFont = SelectObject(hdc, hFont);
8967 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8969 TEXTMETRICW textMetric;
8971 /* Add Extra spacing for the next character */
8972 GetTextMetricsW(hdc, &textMetric);
8973 sz.cx += (textMetric.tmMaxCharWidth * 2);
8975 SetWindowPos (
8976 infoPtr->hwndEdit,
8977 HWND_TOP,
8980 sz.cx,
8981 rect.bottom - rect.top,
8982 SWP_DRAWFRAME|SWP_NOMOVE);
8984 if(hFont != 0)
8985 SelectObject(hdc, hOldFont);
8987 ReleaseDC(infoPtr->hwndSelf, hdc);
8989 break;
8992 default:
8993 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8996 return 0;
9000 /***
9001 * DESCRIPTION:
9002 * Subclassed edit control windproc function
9004 * PARAMETER(S):
9006 * RETURN:
9008 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9009 WPARAM wParam, LPARAM lParam, BOOL isW)
9011 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9012 static BOOL bIgnoreKillFocus = FALSE;
9013 BOOL cancel = FALSE;
9015 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9016 hwnd, uMsg, wParam, lParam, isW);
9018 switch (uMsg)
9020 case WM_GETDLGCODE:
9021 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9023 case WM_KILLFOCUS:
9024 if(bIgnoreKillFocus) return TRUE;
9025 break;
9027 case WM_DESTROY:
9029 WNDPROC editProc = infoPtr->EditWndProc;
9030 infoPtr->EditWndProc = 0;
9031 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9032 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9035 case WM_KEYDOWN:
9036 if (VK_ESCAPE == (INT)wParam)
9038 cancel = TRUE;
9039 break;
9041 else if (VK_RETURN == (INT)wParam)
9042 break;
9044 default:
9045 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9048 if (infoPtr->bEditing)
9050 LPWSTR buffer = NULL;
9052 if (!cancel)
9054 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9056 if (len)
9058 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9060 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9061 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9065 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9066 /* eg. Using a messagebox */
9067 bIgnoreKillFocus = TRUE;
9068 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9070 if (buffer) COMCTL32_Free(buffer);
9072 bIgnoreKillFocus = FALSE;
9075 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9076 return TRUE;
9079 /***
9080 * DESCRIPTION:
9081 * Subclassed edit control windproc function
9083 * PARAMETER(S):
9085 * RETURN:
9087 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9089 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9092 /***
9093 * DESCRIPTION:
9094 * Subclassed edit control windproc function
9096 * PARAMETER(S):
9098 * RETURN:
9100 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9102 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9105 /***
9106 * DESCRIPTION:
9107 * Creates a subclassed edit cotrol
9109 * PARAMETER(S):
9111 * RETURN:
9113 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9114 INT x, INT y, INT width, INT height, BOOL isW)
9116 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9117 HWND hedit;
9118 SIZE sz;
9119 HDC hdc;
9120 HDC hOldFont=0;
9121 TEXTMETRICW textMetric;
9122 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9124 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9126 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9127 hdc = GetDC(infoPtr->hwndSelf);
9129 /* Select the font to get appropriate metric dimensions */
9130 if(infoPtr->hFont != 0)
9131 hOldFont = SelectObject(hdc, infoPtr->hFont);
9133 /*Get String Lenght in pixels */
9134 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9136 /*Add Extra spacing for the next character */
9137 GetTextMetricsW(hdc, &textMetric);
9138 sz.cx += (textMetric.tmMaxCharWidth * 2);
9140 if(infoPtr->hFont != 0)
9141 SelectObject(hdc, hOldFont);
9143 ReleaseDC(infoPtr->hwndSelf, hdc);
9144 if (isW)
9145 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9146 else
9147 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9149 if (!hedit) return 0;
9151 infoPtr->EditWndProc = (WNDPROC)
9152 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9153 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9155 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9157 return hedit;