Fix bug in OWNERDATA selection handling.
[wine.git] / dlls / comctl32 / listview.c
blobbae97240e2dfd96300bee3d9486e95fa29a979c0
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 INT nHotItem;
130 SHORT notifyFormat;
131 RECT rcList; /* This rectangle is really the window
132 * client rectangle possibly reduced by the
133 * horizontal scroll bar and/or header - see
134 * LISTVIEW_UpdateSize. This rectangle offset
135 * by the LISTVIEW_GetOrigin value is within
136 * the rcView rectangle */
137 RECT rcView; /* This rectangle contains all items -
138 * contructed in LISTVIEW_AlignTop and
139 * LISTVIEW_AlignLeft */
140 SIZE iconSize;
141 SIZE iconSpacing;
142 SIZE iconStateSize;
143 UINT uCallbackMask;
144 HWND hwndHeader;
145 HFONT hDefaultFont;
146 HCURSOR hHotCursor;
147 HFONT hFont;
148 INT ntmHeight; /* from GetTextMetrics from above font */
149 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
150 BOOL bFocus;
151 INT nFocusedItem;
152 RECT rcFocus;
153 RECT rcLargeFocus; /* non-empty when a large item in ICON mode has focus */
154 DWORD dwStyle; /* the cached window GWL_STYLE */
155 DWORD dwLvExStyle; /* extended listview style */
156 HDPA hdpaItems;
157 PFNLVCOMPARE pfnCompare;
158 LPARAM lParamSort;
159 HWND hwndEdit;
160 BOOL bEditing;
161 WNDPROC EditWndProc;
162 INT nEditLabelItem;
163 DWORD dwHoverTime;
165 DWORD lastKeyPressTimestamp;
166 WPARAM charCode;
167 INT nSearchParamLength;
168 WCHAR szSearchParam[ MAX_PATH ];
169 BOOL bIsDrawing;
170 } LISTVIEW_INFO;
172 DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO, hwndSelf);
175 * constants
177 /* How many we debug buffer to allocate */
178 #define DEBUG_BUFFERS 20
179 /* The size of a single debug bbuffer */
180 #define DEBUG_BUFFER_SIZE 256
182 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
183 #define SB_INTERNAL -1
185 /* maximum size of a label */
186 #define DISP_TEXT_SIZE 512
188 /* padding for items in list and small icon display modes */
189 #define WIDTH_PADDING 12
191 /* padding for items in list, report and small icon display modes */
192 #define HEIGHT_PADDING 1
194 /* offset of items in report display mode */
195 #define REPORT_MARGINX 2
197 /* padding for icon in large icon display mode
198 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
199 * that HITTEST will see.
200 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
201 * ICON_TOP_PADDING - sum of the two above.
202 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
203 * LABEL_VERT_PADDING - between bottom of text and end of box
205 * ICON_LR_PADDING - additional width above icon size.
206 * ICON_LR_HALF - half of the above value
208 #define ICON_TOP_PADDING_NOTHITABLE 2
209 #define ICON_TOP_PADDING_HITABLE 2
210 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
211 #define ICON_BOTTOM_PADDING 4
212 #define LABEL_VERT_PADDING 7
213 #define ICON_LR_PADDING 16
214 #define ICON_LR_HALF (ICON_LR_PADDING/2)
216 /* default label width for items in list and small icon display modes */
217 #define DEFAULT_LABEL_WIDTH 40
219 /* default column width for items in list display mode */
220 #define DEFAULT_COLUMN_WIDTH 128
222 /* Size of "line" scroll for V & H scrolls */
223 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
225 /* Padding betwen image and label */
226 #define IMAGE_PADDING 2
228 /* Padding behind the label */
229 #define TRAILING_PADDING 5
231 /* Border for the icon caption */
232 #define CAPTION_BORDER 2
234 /* Standard DrawText flags for LISTVIEW_UpdateLargeItemLabelRect and LISTVIEW_DrawLargeItem */
235 #define LISTVIEW_DTFLAGS DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL
238 * macros
240 /* retrieve the number of items in the listview */
241 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
243 #define LISTVIEW_DUMP(iP) do { \
244 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
245 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
246 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
247 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
248 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
249 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
250 (iP->bFocus) ? "true" : "false"); \
251 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
252 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
253 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
254 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
255 iP->hwndSelf, \
256 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
257 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
258 } while(0)
262 * forward declarations
264 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
265 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
266 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
267 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
268 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
269 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *);
270 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
271 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
272 static INT LISTVIEW_GetItemWidth(LISTVIEW_INFO *);
273 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
274 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
275 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
276 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
277 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
278 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
279 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, LONG, LONG);
280 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
281 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
282 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
283 static void LISTVIEW_UnsupportedStyles(LONG);
284 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
285 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
286 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
287 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
288 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *, WPARAM, LPARAM);
289 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
290 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
291 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
292 static BOOL LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *, int, RECT*);
293 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
294 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
295 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
296 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
297 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
298 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
300 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
301 #define KEY_DELAY 450
303 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
306 /******** Text handling functions *************************************/
308 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
309 * text string. The string may be ANSI or Unicode, in which case
310 * the boolean isW tells us the type of the string.
312 * The name of the function tell what type of strings it expects:
313 * W: Unicode, T: ANSI/Unicode - function of isW
316 static inline BOOL is_textW(LPCWSTR text)
318 return text != NULL && text != LPSTR_TEXTCALLBACKW;
321 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
323 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
324 return is_textW(text);
327 static inline int textlenT(LPCWSTR text, BOOL isW)
329 return !is_textT(text, isW) ? 0 :
330 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
333 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
335 if (isDestW)
336 if (isSrcW) lstrcpynW(dest, src, max);
337 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
338 else
339 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
340 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
343 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
345 LPWSTR wstr = (LPWSTR)text;
347 if (!isW && is_textT(text, isW))
349 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
350 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
351 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
353 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
354 return wstr;
357 static inline void textfreeT(LPWSTR wstr, BOOL isW)
359 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
363 * dest is a pointer to a Unicode string
364 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
366 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
368 BOOL bResult = TRUE;
370 if (src == LPSTR_TEXTCALLBACKW)
372 if (is_textW(*dest)) COMCTL32_Free(*dest);
373 *dest = LPSTR_TEXTCALLBACKW;
375 else
377 LPWSTR pszText = textdupTtoW(src, isW);
378 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
379 bResult = Str_SetPtrW(dest, pszText);
380 textfreeT(pszText, isW);
382 return bResult;
386 * compares a Unicode to a Unicode/ANSI text string
388 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
390 if (!aw) return bt ? -1 : 0;
391 if (!bt) return aw ? 1 : 0;
392 if (aw == LPSTR_TEXTCALLBACKW)
393 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
394 if (bt != LPSTR_TEXTCALLBACKW)
396 LPWSTR bw = textdupTtoW(bt, isW);
397 int r = bw ? lstrcmpW(aw, bw) : 1;
398 textfreeT(bw, isW);
399 return r;
402 return 1;
405 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
407 int res;
409 n = min(min(n, strlenW(s1)), strlenW(s2));
410 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
411 return res ? res - sizeof(WCHAR) : res;
414 /******** Debugging functions *****************************************/
416 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
418 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
419 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
422 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
424 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
425 n = min(textlenT(text, isW), n);
426 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
429 static char* debug_getbuf()
431 static int index = 0;
432 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
433 return buffers[index++ % DEBUG_BUFFERS];
436 static inline char* debugrect(const RECT* rect)
438 if (rect) {
439 char* buf = debug_getbuf();
440 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
441 rect->left, rect->top, rect->right, rect->bottom);
442 return buf;
443 } else return "(null)";
446 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
448 char* buf = debug_getbuf(), *text = buf;
449 int len, size = DEBUG_BUFFER_SIZE;
451 if (lpLVItem == NULL) return "(null)";
452 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
453 if (len == -1) goto end; buf += len; size -= len;
454 if (lpLVItem->mask & LVIF_STATE)
455 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
456 else len = 0;
457 if (len == -1) goto end; buf += len; size -= len;
458 if (lpLVItem->mask & LVIF_TEXT)
459 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
460 else len = 0;
461 if (len == -1) goto end; buf += len; size -= len;
462 if (lpLVItem->mask & LVIF_IMAGE)
463 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
464 else len = 0;
465 if (len == -1) goto end; buf += len; size -= len;
466 if (lpLVItem->mask & LVIF_PARAM)
467 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
468 else len = 0;
469 if (len == -1) goto end; buf += len; size -= len;
470 if (lpLVItem->mask & LVIF_INDENT)
471 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
472 else len = 0;
473 if (len == -1) goto end; buf += len; size -= len;
474 goto undo;
475 end:
476 buf = text + strlen(text);
477 undo:
478 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
479 return text;
482 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
484 char* buf = debug_getbuf(), *text = buf;
485 int len, size = DEBUG_BUFFER_SIZE;
487 if (lpColumn == NULL) return "(null)";
488 len = snprintf(buf, size, "{");
489 if (len == -1) goto end; buf += len; size -= len;
490 if (lpColumn->mask & LVCF_SUBITEM)
491 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
492 else len = 0;
493 if (len == -1) goto end; buf += len; size -= len;
494 if (lpColumn->mask & LVCF_FMT)
495 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
496 else len = 0;
497 if (len == -1) goto end; buf += len; size -= len;
498 if (lpColumn->mask & LVCF_WIDTH)
499 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
500 else len = 0;
501 if (len == -1) goto end; buf += len; size -= len;
502 if (lpColumn->mask & LVCF_TEXT)
503 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
504 else len = 0;
505 if (len == -1) goto end; buf += len; size -= len;
506 if (lpColumn->mask & LVCF_IMAGE)
507 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
508 else len = 0;
509 if (len == -1) goto end; buf += len; size -= len;
510 if (lpColumn->mask & LVCF_ORDER)
511 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
512 else len = 0;
513 if (len == -1) goto end; buf += len; size -= len;
514 goto undo;
515 end:
516 buf = text + strlen(text);
517 undo:
518 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
519 return text;
523 /******** Notification functions i************************************/
525 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
527 pnmh->hwndFrom = infoPtr->hwndSelf;
528 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
529 pnmh->code = code;
530 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
531 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
534 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
536 NMHDR nmh;
537 notify(infoPtr, LVN_ITEMACTIVATE, &nmh);
540 static inline BOOL notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
542 return notify(infoPtr, code, (LPNMHDR)plvnm);
545 static int get_ansi_notification(INT unicodeNotificationCode)
547 switch (unicodeNotificationCode)
549 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
550 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
551 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
552 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
553 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
554 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
556 ERR("unknown notification %x\n", unicodeNotificationCode);
557 return unicodeNotificationCode;
561 Send notification. depends on dispinfoW having same
562 structure as dispinfoA.
563 infoPtr : listview struct
564 notificationCode : *Unicode* notification code
565 pdi : dispinfo structure (can be unicode or ansi)
566 isW : TRUE if dispinfo is Unicode
568 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
570 BOOL bResult = FALSE;
571 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
572 INT realNotifCode;
573 INT cchTempBufMax = 0, savCchTextMax = 0;
574 LPWSTR pszTempBuf = NULL, savPszText = NULL;
576 TRACE("(code=%x, pdi=%p, isW=%d)\n", notificationCode, pdi, isW);
577 TRACE(" notifyFormat=%s\n",
578 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
579 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
580 if (infoPtr->notifyFormat == NFR_ANSI)
581 realNotifCode = get_ansi_notification(notificationCode);
582 else
583 realNotifCode = notificationCode;
585 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
587 if (isW && infoPtr->notifyFormat == NFR_ANSI)
588 convertToAnsi = TRUE;
589 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
590 convertToUnicode = TRUE;
593 if (convertToAnsi || convertToUnicode)
595 TRACE(" we have to convert the text to the correct format\n");
596 if (notificationCode != LVN_GETDISPINFOW)
597 { /* length of existing text */
598 cchTempBufMax = convertToUnicode ?
599 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
600 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
602 else
603 cchTempBufMax = pdi->item.cchTextMax;
605 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
606 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
607 if (!pszTempBuf) return FALSE;
608 if (convertToUnicode)
609 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
610 pszTempBuf, cchTempBufMax);
611 else
612 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
613 cchTempBufMax, NULL, NULL);
614 TRACE(" text=%s\n", debugtext_t(pszTempBuf, convertToUnicode));
615 savCchTextMax = pdi->item.cchTextMax;
616 savPszText = pdi->item.pszText;
617 pdi->item.pszText = pszTempBuf;
618 pdi->item.cchTextMax = cchTempBufMax;
621 bResult = notify(infoPtr, realNotifCode, (LPNMHDR)pdi);
623 if (convertToUnicode || convertToAnsi)
624 { /* convert back result */
625 TRACE(" returned text=%s\n", debugtext_t(pdi->item.pszText, convertToUnicode));
626 if (convertToUnicode) /* note : pointer can be changed by app ! */
627 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
628 savCchTextMax, NULL, NULL);
629 else
630 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
631 savPszText, savCchTextMax);
632 pdi->item.pszText = savPszText; /* restores our buffer */
633 pdi->item.cchTextMax = savCchTextMax;
634 HeapFree(GetProcessHeap(), 0, pszTempBuf);
636 return bResult;
639 static inline void notify_odcachehint(LISTVIEW_INFO *infoPtr, INT iFrom, INT iTo)
641 NMLVCACHEHINT nmlv;
643 nmlv.iFrom = iFrom;
644 nmlv.iTo = iTo;
645 notify(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
648 static BOOL notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, HDC hdc, RECT rc)
650 NMLVCUSTOMDRAW nmlvcd;
652 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage, hdc);
654 nmlvcd.nmcd.dwDrawStage = dwDrawStage;
655 nmlvcd.nmcd.hdc = hdc;
656 nmlvcd.nmcd.rc = rc;
657 nmlvcd.nmcd.dwItemSpec = 0;
658 nmlvcd.nmcd.uItemState = 0;
659 nmlvcd.nmcd.lItemlParam = 0;
660 nmlvcd.clrText = infoPtr->clrText;
661 nmlvcd.clrTextBk = infoPtr->clrBk;
663 return (BOOL)notify(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
666 /* FIXME: we should inline this where it's called somehow
667 * I think we need to pass in the structure
669 static BOOL notify_customdrawitem (LISTVIEW_INFO *infoPtr, HDC hdc, UINT iItem, UINT iSubItem, UINT uItemDrawState)
671 NMLVCUSTOMDRAW nmlvcd;
672 UINT uItemState;
673 RECT itemRect;
674 LVITEMW item;
675 BOOL bReturn;
677 item.iItem = iItem;
678 item.iSubItem = 0;
679 item.mask = LVIF_PARAM;
680 if (!LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
682 uItemState = 0;
684 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_SELECTED)) uItemState |= CDIS_SELECTED;
685 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_FOCUSED)) uItemState |= CDIS_FOCUS;
686 if (iItem == infoPtr->nHotItem) uItemState |= CDIS_HOT;
688 itemRect.left = LVIR_BOUNDS;
689 LISTVIEW_GetItemRect(infoPtr, iItem, &itemRect);
691 nmlvcd.nmcd.dwDrawStage = CDDS_ITEM | uItemDrawState;
692 nmlvcd.nmcd.hdc = hdc;
693 nmlvcd.nmcd.rc = itemRect;
694 nmlvcd.nmcd.dwItemSpec = iItem;
695 nmlvcd.nmcd.uItemState = uItemState;
696 nmlvcd.nmcd.lItemlParam = item.lParam;
697 nmlvcd.clrText = infoPtr->clrText;
698 nmlvcd.clrTextBk = infoPtr->clrBk;
699 nmlvcd.iSubItem = iSubItem;
701 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
702 nmlvcd.nmcd.dwDrawStage, nmlvcd.nmcd.hdc, nmlvcd.nmcd.dwItemSpec,
703 nmlvcd.nmcd.uItemState, nmlvcd.nmcd.lItemlParam);
705 bReturn = notify(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
707 infoPtr->clrText = nmlvcd.clrText;
708 infoPtr->clrBk = nmlvcd.clrTextBk;
710 return bReturn;
713 /******** Misc helper functions ************************************/
715 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
716 WPARAM wParam, LPARAM lParam, BOOL isW)
718 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
719 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
722 /******** Internal API functions ************************************/
724 /* The Invalidate* are macros, so we preserve the caller location */
725 #define LISTVIEW_InvalidateRect(infoPtr, rect) do { \
726 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
727 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
728 } while (0)
730 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
731 RECT rcItem; \
732 rcItem.left = LVIR_BOUNDS; \
733 if(LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) \
734 LISTVIEW_InvalidateRect(infoPtr, &rcItem); \
735 } while (0)
737 #define LISTVIEW_InvalidateList(infoPtr)\
738 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcList)
740 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
742 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
745 static inline int LISTVIEW_GetType(LISTVIEW_INFO *infoPtr)
747 return infoPtr->dwStyle & LVS_TYPEMASK;
750 /***
751 * DESCRIPTION:
752 * Retrieves the number of items that can fit vertically in the client area.
754 * PARAMETER(S):
755 * [I] infoPtr : valid pointer to the listview structure
757 * RETURN:
758 * Number of items per row.
760 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
762 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
764 return max(nListWidth/infoPtr->nItemWidth, 1);
767 /***
768 * DESCRIPTION:
769 * Retrieves the number of items that can fit horizontally in the client
770 * area.
772 * PARAMETER(S):
773 * [I] infoPtr : valid pointer to the listview structure
775 * RETURN:
776 * Number of items per column.
778 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
780 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
782 return max(nListHeight / infoPtr->nItemHeight, 1);
785 /***
786 * DESCRIPTION:
787 * Retrieves the range of visible items. Note that the upper limit
788 * may be a bit larger than the actual last visible item.
790 * PARAMETER(S):
791 * [I] infoPtr : valid pointer to the listview structure
793 * RETURN:
794 * maximum range of visible items
796 static RANGE LISTVIEW_GetVisibleRange(LISTVIEW_INFO *infoPtr)
798 UINT uView = LISTVIEW_GetType(infoPtr);
799 INT nPerCol, nPerRow;
800 RANGE visrange;
802 visrange.lower = LISTVIEW_GetTopIndex(infoPtr);
804 if (uView == LVS_REPORT)
806 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
807 nPerRow = 1;
809 else if (uView == LVS_LIST)
811 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
812 nPerRow = LISTVIEW_GetCountPerRow(infoPtr);
814 else
816 /* FIXME: this is correct only in autoarrange mode */
817 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
818 nPerRow = LISTVIEW_GetCountPerRow(infoPtr) + 1;
821 visrange.upper = visrange.lower + nPerCol * nPerRow;
822 if (visrange.upper > GETITEMCOUNT(infoPtr))
823 visrange.upper = GETITEMCOUNT(infoPtr);
825 TRACE("range=(%d, %d)\n", visrange.lower, visrange.upper);
827 return visrange;
831 /*************************************************************************
832 * LISTVIEW_ProcessLetterKeys
834 * Processes keyboard messages generated by pressing the letter keys
835 * on the keyboard.
836 * What this does is perform a case insensitive search from the
837 * current position with the following quirks:
838 * - If two chars or more are pressed in quick succession we search
839 * for the corresponding string (e.g. 'abc').
840 * - If there is a delay we wipe away the current search string and
841 * restart with just that char.
842 * - If the user keeps pressing the same character, whether slowly or
843 * fast, so that the search string is entirely composed of this
844 * character ('aaaaa' for instance), then we search for first item
845 * that starting with that character.
846 * - If the user types the above character in quick succession, then
847 * we must also search for the corresponding string ('aaaaa'), and
848 * go to that string if there is a match.
850 * PARAMETERS
851 * [I] hwnd : handle to the window
852 * [I] charCode : the character code, the actual character
853 * [I] keyData : key data
855 * RETURNS
857 * Zero.
859 * BUGS
861 * - The current implementation has a list of characters it will
862 * accept and it ignores averything else. In particular it will
863 * ignore accentuated characters which seems to match what
864 * Windows does. But I'm not sure it makes sense to follow
865 * Windows there.
866 * - We don't sound a beep when the search fails.
868 * SEE ALSO
870 * TREEVIEW_ProcessLetterKeys
872 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
874 INT nItem;
875 INT nSize;
876 INT endidx,idx;
877 LVITEMW item;
878 WCHAR buffer[MAX_PATH];
879 DWORD timestamp,elapsed;
881 /* simple parameter checking */
882 if (!charCode || !keyData)
883 return 0;
885 if (!infoPtr)
886 return 0;
888 /* only allow the valid WM_CHARs through */
889 if (!isalnum(charCode) &&
890 charCode != '.' && charCode != '`' && charCode != '!' &&
891 charCode != '@' && charCode != '#' && charCode != '$' &&
892 charCode != '%' && charCode != '^' && charCode != '&' &&
893 charCode != '*' && charCode != '(' && charCode != ')' &&
894 charCode != '-' && charCode != '_' && charCode != '+' &&
895 charCode != '=' && charCode != '\\'&& charCode != ']' &&
896 charCode != '}' && charCode != '[' && charCode != '{' &&
897 charCode != '/' && charCode != '?' && charCode != '>' &&
898 charCode != '<' && charCode != ',' && charCode != '~')
899 return 0;
901 nSize=GETITEMCOUNT(infoPtr);
902 /* if there's one item or less, there is no where to go */
903 if (nSize <= 1)
904 return 0;
906 /* compute how much time elapsed since last keypress */
907 timestamp=GetTickCount();
908 if (timestamp > infoPtr->lastKeyPressTimestamp) {
909 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
910 } else {
911 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
914 /* update the search parameters */
915 infoPtr->lastKeyPressTimestamp=timestamp;
916 if (elapsed < KEY_DELAY) {
917 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
918 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
920 if (infoPtr->charCode != charCode) {
921 infoPtr->charCode=charCode=0;
923 } else {
924 infoPtr->charCode=charCode;
925 infoPtr->szSearchParam[0]=charCode;
926 infoPtr->nSearchParamLength=1;
927 /* Redundant with the 1 char string */
928 charCode=0;
931 /* and search from the current position */
932 nItem=-1;
933 if (infoPtr->nFocusedItem >= 0) {
934 endidx=infoPtr->nFocusedItem;
935 idx=endidx;
936 /* if looking for single character match,
937 * then we must always move forward
939 if (infoPtr->nSearchParamLength == 1)
940 idx++;
941 } else {
942 endidx=nSize;
943 idx=0;
945 do {
946 if (idx == nSize) {
947 if (endidx == nSize || endidx == 0)
948 break;
949 idx=0;
952 /* get item */
953 item.mask = LVIF_TEXT;
954 item.iItem = idx;
955 item.iSubItem = 0;
956 item.pszText = buffer;
957 item.cchTextMax = COUNTOF(buffer);
958 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
960 /* check for a match */
961 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
962 nItem=idx;
963 break;
964 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
965 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
966 /* This would work but we must keep looking for a longer match */
967 nItem=idx;
969 idx++;
970 } while (idx != endidx);
972 if (nItem != -1)
973 LISTVIEW_KeySelection(infoPtr, nItem);
975 return 0;
978 /*************************************************************************
979 * LISTVIEW_UpdateHeaderSize [Internal]
981 * Function to resize the header control
983 * PARAMS
984 * hwnd [I] handle to a window
985 * nNewScrollPos [I] Scroll Pos to Set
987 * RETURNS
988 * nothing
990 * NOTES
992 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
994 RECT winRect;
995 POINT point[2];
997 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
999 GetWindowRect(infoPtr->hwndHeader, &winRect);
1000 point[0].x = winRect.left;
1001 point[0].y = winRect.top;
1002 point[1].x = winRect.right;
1003 point[1].y = winRect.bottom;
1005 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1006 point[0].x = -nNewScrollPos;
1007 point[1].x += nNewScrollPos;
1009 SetWindowPos(infoPtr->hwndHeader,0,
1010 point[0].x,point[0].y,point[1].x,point[1].y,
1011 SWP_NOZORDER | SWP_NOACTIVATE);
1014 /***
1015 * DESCRIPTION:
1016 * Update the scrollbars. This functions should be called whenever
1017 * the content, size or view changes.
1019 * PARAMETER(S):
1020 * [I] infoPtr : valid pointer to the listview structure
1022 * RETURN:
1023 * None
1025 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1027 LONG lStyle = infoPtr->dwStyle;
1028 UINT uView = lStyle & LVS_TYPEMASK;
1029 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1030 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1031 SCROLLINFO scrollInfo;
1033 if (lStyle & LVS_NOSCROLL) return;
1035 scrollInfo.cbSize = sizeof(SCROLLINFO);
1037 if (uView == LVS_LIST)
1039 /* update horizontal scrollbar */
1040 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1041 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1042 INT nNumOfItems = GETITEMCOUNT(infoPtr);
1044 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1045 nNumOfItems, nCountPerColumn, nCountPerRow);
1047 scrollInfo.nMin = 0;
1048 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
1049 if((nNumOfItems % nCountPerColumn) == 0)
1050 scrollInfo.nMax--;
1051 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1052 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1053 scrollInfo.nPage = nCountPerRow;
1054 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1055 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1056 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1058 else if (uView == LVS_REPORT)
1060 BOOL test;
1062 /* update vertical scrollbar */
1063 scrollInfo.nMin = 0;
1064 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
1065 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1066 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1067 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1068 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1069 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1070 scrollInfo.nMax, scrollInfo.nPage, test);
1071 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1072 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1074 /* update horizontal scrollbar */
1075 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1076 scrollInfo.fMask = SIF_POS;
1077 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1078 || GETITEMCOUNT(infoPtr) == 0)
1080 scrollInfo.nPos = 0;
1082 scrollInfo.nMin = 0;
1083 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1084 scrollInfo.nPage = nListWidth;
1085 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1086 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1087 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1088 scrollInfo.nMax, scrollInfo.nPage, test);
1089 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1090 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1092 /* Update the Header Control */
1093 scrollInfo.fMask = SIF_POS;
1094 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1095 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1098 else
1100 RECT rcView;
1102 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1104 INT nViewWidth = rcView.right - rcView.left;
1105 INT nViewHeight = rcView.bottom - rcView.top;
1107 /* Update Horizontal Scrollbar */
1108 scrollInfo.fMask = SIF_POS;
1109 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1110 || GETITEMCOUNT(infoPtr) == 0)
1112 scrollInfo.nPos = 0;
1114 scrollInfo.nMin = 0;
1115 scrollInfo.nMax = max(nViewWidth, 0)-1;
1116 scrollInfo.nPage = nListWidth;
1117 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1118 TRACE("LVS_ICON/SMALLICON Horz.\n");
1119 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1121 /* Update Vertical Scrollbar */
1122 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1123 scrollInfo.fMask = SIF_POS;
1124 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1125 || GETITEMCOUNT(infoPtr) == 0)
1127 scrollInfo.nPos = 0;
1129 scrollInfo.nMin = 0;
1130 scrollInfo.nMax = max(nViewHeight,0)-1;
1131 scrollInfo.nPage = nListHeight;
1132 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1133 TRACE("LVS_ICON/SMALLICON Vert.\n");
1134 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1140 /***
1141 * Toggles (draws/erase) the focus rectangle.
1143 static inline void LISTVIEW_ToggleFocusRect(LISTVIEW_INFO *infoPtr)
1145 TRACE("rcFocus=%s\n", debugrect(&infoPtr->rcFocus));
1147 /* if we have a focus rectagle, draw it */
1148 if (!IsRectEmpty(&infoPtr->rcFocus))
1150 HDC hdc = GetDC(infoPtr->hwndSelf);
1151 DrawFocusRect(hdc, &infoPtr->rcFocus);
1152 ReleaseDC(infoPtr->hwndSelf, hdc);
1156 /***
1157 * Invalidates all visible selected items.
1159 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1161 RANGE visrange;
1162 INT i;
1164 visrange = LISTVIEW_GetVisibleRange(infoPtr);
1165 for (i = visrange.lower; i <= visrange.upper; i++)
1167 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
1168 LISTVIEW_InvalidateItem(infoPtr, i);
1173 /***
1174 * DESCRIPTION:
1175 * Prints a message for unsupported window styles.
1176 * A kind of TODO list for window styles.
1178 * PARAMETER(S):
1179 * [I] LONG : window style
1181 * RETURN:
1182 * None
1184 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1186 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1187 FIXME(" LVS_NOSCROLL\n");
1189 if (lStyle & LVS_NOLABELWRAP)
1190 FIXME(" LVS_NOLABELWRAP\n");
1192 if (lStyle & LVS_SORTASCENDING)
1193 FIXME(" LVS_SORTASCENDING\n");
1195 if (lStyle & LVS_SORTDESCENDING)
1196 FIXME(" LVS_SORTDESCENDING\n");
1199 /***
1200 * DESCRIPTION: [INTERNAL]
1201 * Compute sizes and rectangles of an item. This is to localize all
1202 * the computations in one place. If you are not interested in some
1203 * of these values, simply pass in a NULL.
1205 * PARAMETER(S):
1206 * [I] infoPtr : valid pointer to the listview structure
1207 * [I] nItem : item number
1208 * [O] lpptPosition : ptr to Position point
1209 * the Position point is relative to infoPtr->rcList
1210 * (has *NOT* been adjusted by the origin)
1211 * [O] lprcBoundary : ptr to Boundary rectangle
1212 * the Boundary rectangle is relative to infoPtr->rcList
1213 * (has *NOT* been adjusted by the origin)
1214 * [O] lprcIcon : ptr to Icon rectangle
1215 * the Icon rectangle is relative to infoPtr->rcView
1216 * (has already been adjusted by the origin)
1217 * [O] lprcLabel : ptr to Label rectangle
1218 * - the Label rectangle is relative to infoPtr->rcView
1219 * (has already been adjusted by the origin)
1220 * - the Label rectangle encloses the label text only
1221 * [O] lprcText : ptr to FullText rectangle
1222 * - the FullText rectangle is relative to infoPtr->rcView
1223 * (has already been adjusted by the origin)
1224 * - the FullText rectangle contains the Label rectangle
1225 * but may be bigger. Used for filling the background.
1227 * RETURN:
1228 * TRUE if computations OK
1229 * FALSE otherwise
1231 static BOOL LISTVIEW_GetItemMeasures(LISTVIEW_INFO *infoPtr, INT nItem,
1232 LPPOINT lpptPosition,
1233 LPRECT lprcBoundary, LPRECT lprcIcon,
1234 LPRECT lprcLabel, LPRECT lprcText)
1236 LONG lStyle = infoPtr->dwStyle;
1237 UINT uView = lStyle & LVS_TYPEMASK;
1238 POINT Origin, Position, TopLeft;
1239 RECT Icon, Boundary, Label, FullText;
1240 INT nIndent = 0;
1242 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1244 if (uView == LVS_REPORT)
1246 LVITEMW lvItem;
1248 lvItem.mask = LVIF_INDENT;
1249 lvItem.iItem = nItem;
1250 lvItem.iSubItem = 0;
1251 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1253 /* do indent */
1254 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
1257 /************************************************************/
1258 /* compute the top, left corner of the absolute boundry box */
1259 /************************************************************/
1260 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1262 /* FIXME: what about virtual listview? */
1263 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
1264 LISTVIEW_ITEM * lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
1265 if (!lpItem) return FALSE;
1266 TopLeft = lpItem->ptPosition;
1268 else if (uView == LVS_LIST)
1270 INT nCountPerColumn;
1271 INT nRow, adjItem;
1273 adjItem = nItem - LISTVIEW_GetTopIndex(infoPtr);
1274 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1275 if (adjItem < 0)
1277 nRow = adjItem % nCountPerColumn;
1278 if (nRow == 0)
1280 TopLeft.x = adjItem / nCountPerColumn * infoPtr->nItemWidth;
1281 TopLeft.y = 0; /*FIXME: how so? */
1283 else
1285 TopLeft.x = (adjItem / nCountPerColumn -1) * infoPtr->nItemWidth;
1286 TopLeft.y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
1289 else
1291 TopLeft.x = adjItem / nCountPerColumn * infoPtr->nItemWidth;
1292 TopLeft.y = adjItem % nCountPerColumn * infoPtr->nItemHeight;
1295 else /* LVS_REPORT */
1297 TopLeft.x = REPORT_MARGINX;
1298 TopLeft.y = ((nItem - LISTVIEW_GetTopIndex(infoPtr)) *
1299 infoPtr->nItemHeight) + infoPtr->rcList.top;
1301 if (!(lStyle & LVS_NOSCROLL))
1302 { /*FIXME: why not use Origin? */
1303 SCROLLINFO scrollInfo;
1305 scrollInfo.cbSize = sizeof(SCROLLINFO);
1306 scrollInfo.fMask = SIF_POS;
1308 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
1309 TopLeft.x -= scrollInfo.nPos;
1313 /************************************************************/
1314 /* compute position point (ala LVM_GETITEMPOSITION) */
1315 /************************************************************/
1316 Position.x = TopLeft.x;
1317 Position.y = TopLeft.y;
1318 if (uView == LVS_ICON)
1320 Position.y += ICON_TOP_PADDING;
1321 Position.x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1323 if (lpptPosition) *lpptPosition = Position;
1324 TRACE("hwnd=%x, item=%d, position=(%ld,%ld)\n",
1325 infoPtr->hwndSelf, nItem, Position.x, Position.y);
1327 /************************************************************/
1328 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1329 /************************************************************/
1330 if (uView == LVS_ICON)
1332 if (infoPtr->himlNormal != NULL)
1334 Icon.left = Position.x + Origin.x - ICON_LR_HALF;
1335 Icon.top = Position.y + Origin.y - ICON_TOP_PADDING;
1336 Icon.right = Icon.left + infoPtr->iconSize.cx + ICON_LR_PADDING;
1337 Icon.bottom = Icon.top + infoPtr->iconSize.cy + ICON_TOP_PADDING;
1339 else return FALSE;
1341 else if (uView == LVS_SMALLICON)
1343 Icon.left = Position.x + Origin.x;
1344 Icon.top = Position.y + Origin.y;
1345 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1347 if (infoPtr->himlState != NULL)
1348 Icon.left += infoPtr->iconStateSize.cx;
1350 Icon.right = Icon.left;
1351 if (infoPtr->himlSmall != NULL)
1352 Icon.right += infoPtr->iconSize.cx;
1354 else /* LVS_LIST or LVS_REPORT */
1356 /* FIXME: why is the one above relative to origin??? */
1357 Icon.left = Position.x;
1358 Icon.top = Position.y;
1359 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1361 if (uView & LVS_REPORT)
1362 Icon.left += nIndent;
1364 if (infoPtr->himlState != NULL)
1365 Icon.left += infoPtr->iconStateSize.cx;
1367 Icon.right = Icon.left;
1368 if (infoPtr->himlSmall != NULL)
1369 Icon.right += infoPtr->iconSize.cx;
1372 if(lprcIcon) *lprcIcon = Icon;
1373 TRACE("hwnd=%x, item=%d, icon=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Icon));
1375 /************************************************************/
1376 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1377 /************************************************************/
1378 if (uView == LVS_ICON)
1380 if (infoPtr->himlNormal != NULL)
1382 INT nLabelWidth;
1384 Label.left = TopLeft.x + Origin.x;
1385 Label.top = TopLeft.y + Origin.y + ICON_TOP_PADDING_HITABLE +
1386 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1388 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1389 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
1391 Label.left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
1392 Label.right = Label.left + nLabelWidth;
1393 Label.bottom = Label.top + infoPtr->ntmHeight + 1;
1394 Label.bottom += HEIGHT_PADDING;
1396 else
1398 Label.right = Label.left + infoPtr->nItemWidth;
1399 Label.bottom = Label.top + infoPtr->nItemHeight + HEIGHT_PADDING;
1400 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, &Label);
1402 FullText = Label;
1403 InflateRect(&FullText, 2, 0);
1404 if (lprcLabel) *lprcLabel = Label;
1405 if (lprcText) *lprcText = FullText;
1407 else return FALSE;
1409 else if (uView == LVS_SMALLICON)
1411 INT nLeftPos, nLabelWidth;
1413 nLeftPos = Label.left = Position.x + Origin.x;
1414 Label.top = Position.y + Origin.y;
1415 Label.bottom = Label.top + infoPtr->nItemHeight;
1417 if (infoPtr->himlState != NULL)
1418 Label.left += infoPtr->iconStateSize.cx;
1420 if (infoPtr->himlSmall != NULL)
1421 Label.left += infoPtr->iconSize.cx;
1423 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1424 nLabelWidth += TRAILING_PADDING;
1425 if (Label.left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
1426 Label.right = Label.left + nLabelWidth;
1427 else
1428 Label.right = nLeftPos + infoPtr->nItemWidth;
1429 if (lprcLabel) *lprcLabel = Label;
1430 if (lprcText) *lprcText = Label;
1432 else /* LVS_LIST or LVS_REPORT */
1434 INT nLabelWidth;
1436 if (uView != LVS_REPORT)
1438 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1439 nLabelWidth += TRAILING_PADDING;
1440 if (infoPtr->himlSmall) nLabelWidth += IMAGE_PADDING;
1442 else
1443 nLabelWidth = LISTVIEW_GetColumnWidth(infoPtr, 0) - Icon.left;
1445 Label.left = Icon.right;
1446 Label.top = Position.y;
1447 Label.right = Label.left + nLabelWidth;
1448 Label.bottom = Label.top + infoPtr->nItemHeight;
1450 if (Label.right - Position.x > infoPtr->nItemWidth)
1451 Label.right = Position.x + infoPtr->nItemWidth;
1452 if (lprcLabel) *lprcLabel = Label;
1453 if (lprcText) *lprcText = Label;
1456 TRACE("hwnd=%x, item=%d, label=%s, fulltext=%s\n",
1457 infoPtr->hwndSelf, nItem, debugrect(&Label), debugrect(lprcText));
1459 /***********************************************************/
1460 /* compute boundary box for the item (ala LVM_GETITEMRECT) */
1461 /***********************************************************/
1463 if (uView == LVS_REPORT)
1465 Boundary.left = TopLeft.x;
1466 Boundary.top = TopLeft.y;
1467 Boundary.right = Boundary.left + infoPtr->nItemWidth;
1468 Boundary.bottom = Boundary.top + infoPtr->nItemHeight;
1469 if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
1470 Boundary.left += nIndent;
1471 Boundary.right -= REPORT_MARGINX;
1472 if (Boundary.right < Boundary.left) Boundary.right = Boundary.left;
1474 else
1476 UnionRect(&Boundary, &Icon, &Label);
1479 TRACE("hwnd=%x, item=%d, boundary=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Boundary));
1480 if (lprcBoundary) *lprcBoundary = Boundary;
1482 return TRUE;
1485 /***
1486 * DESCRIPTION:
1487 * Aligns the items with the top edge of the window.
1489 * PARAMETER(S):
1490 * [I] infoPtr : valid pointer to the listview structure
1492 * RETURN:
1493 * None
1495 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1497 UINT uView = LISTVIEW_GetType(infoPtr);
1498 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1499 POINT ptItem;
1500 RECT rcView;
1501 INT i, off_x=0, off_y=0;
1503 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1505 /* Since SetItemPosition uses upper-left of icon, and for
1506 style=LVS_ICON the icon is not left adjusted, get the offset */
1507 if (uView == LVS_ICON)
1509 off_y = ICON_TOP_PADDING;
1510 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1512 ptItem.x = off_x;
1513 ptItem.y = off_y;
1514 ZeroMemory(&rcView, sizeof(RECT));
1515 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1516 off_x, off_y,
1517 infoPtr->rcList.left, infoPtr->rcList.right);
1519 if (nListWidth > infoPtr->nItemWidth)
1521 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1523 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1525 ptItem.x = off_x;
1526 ptItem.y += infoPtr->nItemHeight;
1529 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1530 ptItem.x += infoPtr->nItemWidth;
1531 rcView.right = max(rcView.right, ptItem.x);
1534 rcView.right -= off_x;
1535 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1537 else
1539 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1541 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1542 ptItem.y += infoPtr->nItemHeight;
1545 rcView.right = infoPtr->nItemWidth;
1546 rcView.bottom = ptItem.y-off_y;
1549 infoPtr->rcView = rcView;
1553 /***
1554 * DESCRIPTION:
1555 * Aligns the items with the left edge of the window.
1557 * PARAMETER(S):
1558 * [I] infoPtr : valid pointer to the listview structure
1560 * RETURN:
1561 * None
1563 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1565 UINT uView = LISTVIEW_GetType(infoPtr);
1566 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1567 POINT ptItem;
1568 RECT rcView;
1569 INT i, off_x=0, off_y=0;
1571 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1573 /* Since SetItemPosition uses upper-left of icon, and for
1574 style=LVS_ICON the icon is not left adjusted, get the offset */
1575 if (uView == LVS_ICON)
1577 off_y = ICON_TOP_PADDING;
1578 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1580 ptItem.x = off_x;
1581 ptItem.y = off_y;
1582 ZeroMemory(&rcView, sizeof(RECT));
1583 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1585 if (nListHeight > infoPtr->nItemHeight)
1587 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1589 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1591 ptItem.y = off_y;
1592 ptItem.x += infoPtr->nItemWidth;
1595 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1596 ptItem.y += infoPtr->nItemHeight;
1597 rcView.bottom = max(rcView.bottom, ptItem.y);
1600 rcView.right = ptItem.x + infoPtr->nItemWidth;
1602 else
1604 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1606 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1607 ptItem.x += infoPtr->nItemWidth;
1610 rcView.bottom = infoPtr->nItemHeight;
1611 rcView.right = ptItem.x;
1614 infoPtr->rcView = rcView;
1619 /***
1620 * DESCRIPTION:
1621 * Retrieves the bounding rectangle of all the items.
1623 * PARAMETER(S):
1624 * [I] infoPtr : valid pointer to the listview structure
1625 * [O] lprcView : bounding rectangle
1627 * RETURN:
1628 * SUCCESS : TRUE
1629 * FAILURE : FALSE
1631 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1633 POINT ptOrigin;
1635 TRACE("(lprcView=%p)\n", lprcView);
1637 if (!lprcView) return FALSE;
1639 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1641 *lprcView = infoPtr->rcView;
1642 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1644 TRACE("lprcView=%s\n", debugrect(lprcView));
1646 return TRUE;
1649 /***
1650 * DESCRIPTION:
1651 * Retrieves the subitem pointer associated with the subitem index.
1653 * PARAMETER(S):
1654 * [I] HDPA : DPA handle for a specific item
1655 * [I] INT : index of subitem
1657 * RETURN:
1658 * SUCCESS : subitem pointer
1659 * FAILURE : NULL
1661 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1662 INT nSubItem)
1664 LISTVIEW_SUBITEM *lpSubItem;
1665 INT i;
1667 /* we should binary search here if need be */
1668 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1670 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1671 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1672 return lpSubItem;
1675 return NULL;
1679 /***
1680 * DESCRIPTION:
1681 * Calculates the width of a specific item.
1683 * PARAMETER(S):
1684 * [I] infoPtr : valid pointer to the listview structure
1685 * [I] nItem : item to calculate width, or -1 for max of all
1687 * RETURN:
1688 * Returns the width of an item width an item.
1690 static INT LISTVIEW_CalculateWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1692 UINT uView = LISTVIEW_GetType(infoPtr);
1693 INT nItemWidth = 0, i;
1695 if (uView == LVS_ICON)
1696 nItemWidth = infoPtr->iconSpacing.cx;
1697 else if (uView == LVS_REPORT)
1699 INT nHeaderItemCount;
1700 RECT rcHeaderItem;
1702 /* calculate width of header */
1703 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1704 for (i = 0; i < nHeaderItemCount; i++)
1705 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
1706 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1708 else
1710 INT nLabelWidth;
1712 if (GETITEMCOUNT(infoPtr) == 0) return DEFAULT_COLUMN_WIDTH;
1714 /* get width of string */
1715 if (nItem == -1)
1717 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1719 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1720 nItemWidth = max(nItemWidth, nLabelWidth);
1723 else
1724 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1725 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
1726 nItemWidth += WIDTH_PADDING;
1727 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
1728 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
1729 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1732 return max(nItemWidth, 1);
1735 /***
1736 * DESCRIPTION:
1737 * Calculates the max width of any item in the list.
1739 * PARAMETER(S):
1740 * [I] infoPtr : valid pointer to the listview structure
1741 * [I] LONG : window style
1743 * RETURN:
1744 * Returns item width.
1746 static inline INT LISTVIEW_GetItemWidth(LISTVIEW_INFO *infoPtr)
1748 return LISTVIEW_CalculateWidth(infoPtr, -1);
1751 /***
1752 * DESCRIPTION:
1753 * Retrieves and saves important text metrics info for the current
1754 * Listview font.
1756 * PARAMETER(S):
1757 * [I] infoPtr : valid pointer to the listview structure
1760 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1762 TEXTMETRICW tm;
1763 HDC hdc = GetDC(infoPtr->hwndSelf);
1764 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1765 INT oldHeight, oldACW;
1767 GetTextMetricsW(hdc, &tm);
1769 oldHeight = infoPtr->ntmHeight;
1770 oldACW = infoPtr->ntmAveCharWidth;
1771 infoPtr->ntmHeight = tm.tmHeight;
1772 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1774 SelectObject(hdc, hOldFont);
1775 ReleaseDC(infoPtr->hwndSelf, hdc);
1776 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1777 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1781 /***
1782 * DESCRIPTION:
1783 * Calculates the height of an item.
1785 * PARAMETER(S):
1786 * [I] infoPtr : valid pointer to the listview structure
1788 * RETURN:
1789 * Returns item height.
1791 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1793 INT nItemHeight;
1795 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
1796 nItemHeight = infoPtr->iconSpacing.cy;
1797 else
1799 nItemHeight = infoPtr->ntmHeight;
1800 if (infoPtr->himlState)
1801 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
1802 if (infoPtr->himlSmall)
1803 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
1804 if (infoPtr->himlState || infoPtr->himlSmall)
1805 nItemHeight += HEIGHT_PADDING;
1807 return nItemHeight;
1810 #if 0
1811 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1813 INT i;
1815 ERR("Selections are:\n");
1816 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
1818 RANGE *selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
1819 ERR(" [%d - %d]\n", selection->lower, selection->upper);
1822 #endif
1824 /***
1825 * DESCRIPTION:
1826 * A compare function for selection ranges
1828 *PARAMETER(S)
1829 * [I] range1 : pointer to selection range 1;
1830 * [I] range2 : pointer to selection range 2;
1831 * [I] flags : flags
1833 *RETURNS:
1834 * >0 : if Item 1 > Item 2
1835 * <0 : if Item 2 > Item 1
1836 * 0 : if Item 1 == Item 2
1838 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2, LPARAM flags)
1840 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
1841 return -1;
1842 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
1843 return 1;
1844 return 0;
1847 /***
1848 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
1850 static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1852 RANGE selection;
1853 LVITEMW lvItem;
1854 INT index, i;
1856 TRACE("range (%i - %i)\n", lower, upper);
1858 /* try find overlapping selections first */
1859 selection.lower = lower - 1;
1860 selection.upper = upper + 1;
1861 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1862 LISTVIEW_CompareSelectionRanges, 0, 0);
1864 if (index == -1)
1866 RANGE *newsel;
1868 /* create the brand new selection to insert */
1869 newsel = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
1870 if(!newsel) return FALSE;
1871 newsel->lower = lower;
1872 newsel->upper = upper;
1874 /* figure out where to insert it */
1875 index = DPA_Search(infoPtr->hdpaSelectionRanges, newsel, 0,
1876 LISTVIEW_CompareSelectionRanges, 0, DPAS_INSERTAFTER);
1877 if (index == -1) index = 0;
1879 /* and get it over with */
1880 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
1882 else
1884 RANGE *chksel, *mrgsel;
1885 INT fromindex, mergeindex;
1887 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1888 if (!chksel) return FALSE;
1889 TRACE("Merge with index %i (%d - %d)\n",
1890 index, chksel->lower, chksel->upper);
1892 chksel->lower = min(lower, chksel->lower);
1893 chksel->upper = max(upper, chksel->upper);
1895 TRACE("New range %i (%d - %d)\n",
1896 index, chksel->lower, chksel->upper);
1898 /* merge now common selection ranges */
1899 fromindex = 0;
1900 selection.lower = chksel->lower - 1;
1901 selection.upper = chksel->upper + 1;
1905 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, fromindex,
1906 LISTVIEW_CompareSelectionRanges, 0, 0);
1907 if (mergeindex == -1) break;
1908 if (mergeindex == index)
1910 fromindex = index + 1;
1911 continue;
1914 TRACE("Merge with index %i\n", mergeindex);
1916 mrgsel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, mergeindex);
1917 if (!mrgsel) return FALSE;
1919 chksel->lower = min(chksel->lower, mrgsel->lower);
1920 chksel->upper = max(chksel->upper, mrgsel->upper);
1921 COMCTL32_Free(mrgsel);
1922 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, mergeindex);
1923 if (mergeindex < index) index --;
1924 } while(1);
1927 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
1929 if (adj_sel_only) return TRUE;
1931 /* set the selection on items */
1932 lvItem.state = LVIS_SELECTED;
1933 lvItem.stateMask = LVIS_SELECTED;
1934 for(i = lower; i <= upper; i++)
1935 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
1937 return TRUE;
1940 /***
1941 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
1943 static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1945 RANGE remsel, tmpsel, *chksel;
1946 BOOL done = FALSE;
1947 LVITEMW lvItem;
1948 INT index, i;
1950 lvItem.state = 0;
1951 lvItem.stateMask = LVIS_SELECTED;
1953 remsel.lower = lower;
1954 remsel.upper = upper;
1956 TRACE("range: (%d - %d)\n", remsel.lower, remsel.upper);
1960 index = DPA_Search(infoPtr->hdpaSelectionRanges, &remsel, 0,
1961 LISTVIEW_CompareSelectionRanges, 0, 0);
1962 if (index == -1) return TRUE;
1964 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1965 if (!chksel) return FALSE;
1967 TRACE("Matches range index %i (%d - %d)\n",
1968 index, chksel->lower, chksel->upper);
1970 /* case 1: Same range */
1971 if ( (chksel->upper == remsel.upper) &&
1972 (chksel->lower == remsel.lower) )
1974 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1975 done = TRUE;
1977 /* case 2: engulf */
1978 else if ( (chksel->upper <= remsel.upper) &&
1979 (chksel->lower >= remsel.lower) )
1981 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1983 /* case 3: overlap upper */
1984 else if ( (chksel->upper < remsel.upper) &&
1985 (chksel->lower < remsel.lower) )
1987 chksel->upper = remsel.lower - 1;
1989 /* case 4: overlap lower */
1990 else if ( (chksel->upper > remsel.upper) &&
1991 (chksel->lower > remsel.lower) )
1993 chksel->lower = remsel.upper + 1;
1995 /* case 5: fully internal */
1996 else
1998 RANGE *newsel =
1999 (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2000 if (!newsel) return FALSE;
2001 tmpsel = *chksel;
2002 newsel->lower = chksel->lower;
2003 newsel->upper = remsel.lower - 1;
2004 chksel->lower = remsel.upper + 1;
2005 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
2006 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
2007 chksel = &tmpsel;
2010 if (adj_sel_only) continue;
2012 /* here, chksel holds the selection to delete */
2013 for (i = chksel->lower; i <= chksel->upper; i++)
2014 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2016 while(!done);
2018 return TRUE;
2022 * DESCRIPTION:
2023 * Adds a selection range.
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_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2036 return add_selection_range(infoPtr, lower, upper, FALSE);
2039 /***
2040 * DESCRIPTION:
2041 * Removes a range selections.
2043 * PARAMETER(S):
2044 * [I] infoPtr : valid pointer to the listview structure
2045 * [I] lower : lower item index
2046 * [I] upper : upper item index
2048 * RETURN:
2049 * Success: TRUE
2050 * Failure: FALSE
2052 static inline BOOL LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2054 return remove_selection_range(infoPtr, lower, upper, FALSE);
2057 /***
2058 * DESCRIPTION:
2059 * Removes all selection ranges
2061 * Parameters(s):
2062 * [I] infoPtr : valid pointer to the listview structure
2064 * RETURNS:
2065 * SUCCESS : TRUE
2066 * FAILURE : TRUE
2068 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
2070 RANGE *sel;
2071 static BOOL removing_all_selections = FALSE;
2073 if (removing_all_selections) return TRUE;
2075 removing_all_selections = TRUE;
2077 TRACE("()\n");
2081 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, 0);
2082 if (sel) LISTVIEW_RemoveSelectionRange(infoPtr, sel->lower, sel->upper);
2084 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
2086 removing_all_selections = FALSE;
2088 return TRUE;
2091 /***
2092 * DESCRIPTION:
2093 * Retrieves the number of items that are marked as selected.
2095 * PARAMETER(S):
2096 * [I] infoPtr : valid pointer to the listview structure
2098 * RETURN:
2099 * Number of items selected.
2101 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2103 INT i, nSelectedCount = 0;
2105 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2107 INT i;
2108 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
2110 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2111 nSelectedCount++;
2114 else
2116 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2118 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2119 nSelectedCount += sel->upper - sel->lower + 1;
2123 TRACE("nSelectedCount=%d\n", nSelectedCount);
2124 return nSelectedCount;
2127 /***
2128 * DESCRIPTION:
2129 * Manages the item focus.
2131 * PARAMETER(S):
2132 * [I] infoPtr : valid pointer to the listview structure
2133 * [I] INT : item index
2135 * RETURN:
2136 * TRUE : focused item changed
2137 * FALSE : focused item has NOT changed
2139 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2141 INT oldFocus = infoPtr->nFocusedItem;
2142 LVITEMW lvItem;
2144 lvItem.state = LVIS_FOCUSED;
2145 lvItem.stateMask = LVIS_FOCUSED;
2146 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2148 return oldFocus != infoPtr->nFocusedItem;
2152 * DESCRIPTION:
2153 * Updates the various indices after an item has been inserted or deleted.
2155 * PARAMETER(S):
2156 * [I] infoPtr : valid pointer to the listview structure
2157 * [I] nItem : item index
2158 * [I] direction : Direction of shift, +1 or -1.
2160 * RETURN:
2161 * None
2163 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2165 RANGE selection,*checkselection;
2166 INT index;
2168 TRACE("Shifting %iu, %i steps\n",nItem,direction);
2170 selection.upper = nItem;
2171 selection.lower = nItem;
2173 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
2174 LISTVIEW_CompareSelectionRanges,
2175 0,DPAS_SORTED|DPAS_INSERTAFTER);
2177 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
2179 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
2180 if ((checkselection->lower >= nItem)&&
2181 ((int)(checkselection->lower + direction) >= 0))
2182 checkselection->lower += direction;
2183 if ((checkselection->upper >= nItem)&&
2184 ((int)(checkselection->upper + direction) >= 0))
2185 checkselection->upper += direction;
2186 index ++;
2189 /* Note that the following will fail if direction != +1 and -1 */
2190 if (infoPtr->nSelectionMark > nItem)
2191 infoPtr->nSelectionMark += direction;
2192 else if (infoPtr->nSelectionMark == nItem)
2194 if (direction > 0)
2195 infoPtr->nSelectionMark += direction;
2196 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
2197 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
2200 if (infoPtr->nFocusedItem > nItem)
2201 infoPtr->nFocusedItem += direction;
2202 else if (infoPtr->nFocusedItem == nItem)
2204 if (direction > 0)
2205 infoPtr->nFocusedItem += direction;
2206 else
2208 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
2209 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
2210 if (infoPtr->nFocusedItem >= 0)
2211 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
2214 /* But we are not supposed to modify nHotItem! */
2219 * DESCRIPTION:
2220 * Adds a block of selections.
2222 * PARAMETER(S):
2223 * [I] infoPtr : valid pointer to the listview structure
2224 * [I] INT : item index
2226 * RETURN:
2227 * None
2229 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2231 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2232 INT nLast = max(infoPtr->nSelectionMark, nItem);
2233 INT i;
2234 LVITEMW item;
2236 if (nFirst == -1)
2237 nFirst = nItem;
2239 item.state = LVIS_SELECTED;
2240 item.stateMask = LVIS_SELECTED;
2242 /* FIXME: this is not correct LVS_OWNERDATA
2243 * See docu for LVN_ITEMCHANGED. Is there something similar for
2244 * RemoveGroupSelection (is there such a thing?)?
2246 for (i = nFirst; i <= nLast; i++)
2247 LISTVIEW_SetItemState(infoPtr,i,&item);
2249 LISTVIEW_SetItemFocus(infoPtr, nItem);
2250 infoPtr->nSelectionMark = nItem;
2254 /***
2255 * DESCRIPTION:
2256 * Sets a single group selection.
2258 * PARAMETER(S):
2259 * [I] infoPtr : valid pointer to the listview structure
2260 * [I] INT : item index
2262 * RETURN:
2263 * None
2265 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2267 UINT uView = LISTVIEW_GetType(infoPtr);
2268 INT i;
2269 LVITEMW item;
2270 POINT ptItem;
2271 RECT rcSel;
2273 LISTVIEW_RemoveAllSelections(infoPtr);
2275 item.state = LVIS_SELECTED;
2276 item.stateMask = LVIS_SELECTED;
2278 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2280 INT nFirst, nLast;
2282 if (infoPtr->nSelectionMark == -1)
2283 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2284 else
2286 nFirst = min(infoPtr->nSelectionMark, nItem);
2287 nLast = max(infoPtr->nSelectionMark, nItem);
2289 for (i = nFirst; i < nLast; i++)
2290 LISTVIEW_SetItemState(infoPtr, i, &item);
2292 else
2294 RECT rcItem, rcSelMark;
2296 rcItem.left = LVIR_BOUNDS;
2297 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2298 rcSelMark.left = LVIR_BOUNDS;
2299 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2300 UnionRect(&rcSel, &rcItem, &rcSelMark);
2301 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2303 LISTVIEW_GetItemPosition(infoPtr, i, &ptItem);
2304 if (PtInRect(&rcSel, ptItem))
2305 LISTVIEW_SetItemState(infoPtr, i, &item);
2309 LISTVIEW_SetItemFocus(infoPtr, nItem);
2312 /***
2313 * DESCRIPTION:
2314 * Sets a single selection.
2316 * PARAMETER(S):
2317 * [I] infoPtr : valid pointer to the listview structure
2318 * [I] INT : item index
2320 * RETURN:
2321 * None
2323 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2325 LVITEMW lvItem;
2327 TRACE("nItem=%d\n", nItem);
2329 LISTVIEW_RemoveAllSelections(infoPtr);
2331 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2332 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2333 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2335 infoPtr->nSelectionMark = nItem;
2338 /***
2339 * DESCRIPTION:
2340 * Set selection(s) with keyboard.
2342 * PARAMETER(S):
2343 * [I] infoPtr : valid pointer to the listview structure
2344 * [I] INT : item index
2346 * RETURN:
2347 * SUCCESS : TRUE (needs to be repainted)
2348 * FAILURE : FALSE (nothing has changed)
2350 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2352 /* FIXME: pass in the state */
2353 LONG lStyle = infoPtr->dwStyle;
2354 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2355 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2356 BOOL bResult = FALSE;
2358 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2360 if (lStyle & LVS_SINGLESEL)
2362 bResult = TRUE;
2363 LISTVIEW_SetSelection(infoPtr, nItem);
2364 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2366 else
2368 if (wShift)
2370 bResult = TRUE;
2371 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2373 else if (wCtrl)
2375 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2377 else
2379 bResult = TRUE;
2380 LISTVIEW_SetSelection(infoPtr, nItem);
2381 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2386 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2387 return bResult;
2390 /***
2391 * DESCRIPTION:
2392 * Selects an item based on coordinates.
2394 * PARAMETER(S):
2395 * [I] infoPtr : valid pointer to the listview structure
2396 * [I] pt : mouse click ccordinates
2398 * RETURN:
2399 * SUCCESS : item index
2400 * FAILURE : -1
2402 static INT LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, POINT pt)
2404 RANGE visrange;
2405 RECT rcItem;
2406 INT i;
2408 visrange = LISTVIEW_GetVisibleRange(infoPtr);
2409 for (i = visrange.lower; i <= visrange.upper; i++)
2411 rcItem.left = LVIR_SELECTBOUNDS;
2412 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
2414 TRACE("i=%d, rcItem=%s\n", i, debugrect(&rcItem));
2415 if (PtInRect(&rcItem, pt)) return i;
2418 return -1;
2421 /***
2422 * DESCRIPTION:
2423 * Called when the mouse is being actively tracked and has hovered for a specified
2424 * amount of time
2426 * PARAMETER(S):
2427 * [I] infoPtr : valid pointer to the listview structure
2428 * [I] fwKeys : key indicator
2429 * [I] pts : mouse position
2431 * RETURN:
2432 * 0 if the message was processed, non-zero if there was an error
2434 * INFO:
2435 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2436 * over the item for a certain period of time.
2439 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2441 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2442 /* FIXME: select the item!!! */
2443 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2445 return 0;
2448 /***
2449 * DESCRIPTION:
2450 * Called whenever WM_MOUSEMOVE is received.
2452 * PARAMETER(S):
2453 * [I] infoPtr : valid pointer to the listview structure
2454 * [I] fwKeys : key indicator
2455 * [I] pts : mouse position
2457 * RETURN:
2458 * 0 if the message is processed, non-zero if there was an error
2460 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2462 TRACKMOUSEEVENT trackinfo;
2464 /* see if we are supposed to be tracking mouse hovering */
2465 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2466 /* fill in the trackinfo struct */
2467 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2468 trackinfo.dwFlags = TME_QUERY;
2469 trackinfo.hwndTrack = infoPtr->hwndSelf;
2470 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2472 /* see if we are already tracking this hwnd */
2473 _TrackMouseEvent(&trackinfo);
2475 if(!(trackinfo.dwFlags & TME_HOVER)) {
2476 trackinfo.dwFlags = TME_HOVER;
2478 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2479 _TrackMouseEvent(&trackinfo);
2483 return 0;
2487 /***
2488 * Tests wheather the item is assignable to a list with style lStyle
2490 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2492 if ( (lpLVItem->mask & LVIF_TEXT) &&
2493 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2494 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2496 return TRUE;
2499 /***
2500 * DESCRIPTION:
2501 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2503 * PARAMETER(S):
2504 * [I] infoPtr : valid pointer to the listview structure
2505 * [I] lpLVItem : valid pointer to new item atttributes
2506 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2508 * RETURN:
2509 * SUCCESS : TRUE
2510 * FAILURE : FALSE
2512 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2514 LONG lStyle = infoPtr->dwStyle;
2515 NMLISTVIEW nmlv;
2516 INT oldState;
2518 /* a virtual listview stores only the state for the main item */
2519 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2521 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2522 TRACE("oldState=%x, newState=%x, uCallbackMask=%x\n",
2523 oldState, lpLVItem->state, infoPtr->uCallbackMask);
2525 /* we're done if we don't need to change anything we handle */
2526 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2527 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2530 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2531 * by LVS_OWERNDATA list controls
2534 /* if we handle the focus, and we're asked to change it, do it now */
2535 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2537 if (lpLVItem->state & LVIS_FOCUSED)
2538 infoPtr->nFocusedItem = lpLVItem->iItem;
2539 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2540 infoPtr->nFocusedItem = -1;
2543 /* and the selection is the only other state a virtual list may hold */
2544 if (lpLVItem->stateMask & LVIS_SELECTED)
2546 if (lpLVItem->state & LVIS_SELECTED)
2548 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2549 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2551 else
2552 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2555 /* notify the parent now that things have changed */
2556 ZeroMemory(&nmlv, sizeof(nmlv));
2557 nmlv.iItem = lpLVItem->iItem;
2558 nmlv.uNewState = lpLVItem->state;
2559 nmlv.uOldState = oldState;
2560 nmlv.uChanged = LVIF_STATE;
2561 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2563 return TRUE;
2566 /***
2567 * DESCRIPTION:
2568 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2570 * PARAMETER(S):
2571 * [I] infoPtr : valid pointer to the listview structure
2572 * [I] lpLVItem : valid pointer to new item atttributes
2573 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2575 * RETURN:
2576 * SUCCESS : TRUE
2577 * FAILURE : FALSE
2579 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2581 LONG lStyle = infoPtr->dwStyle;
2582 UINT uView = lStyle & LVS_TYPEMASK;
2583 HDPA hdpaSubItems;
2584 LISTVIEW_ITEM *lpItem;
2585 NMLISTVIEW nmlv;
2586 UINT uChanged = 0;
2588 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2589 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2591 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2592 if (!lpItem) return FALSE;
2594 /* determine what fields will change */
2595 if ((lpLVItem->mask & LVIF_STATE) &&
2596 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask))
2597 uChanged |= LVIF_STATE;
2599 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2600 uChanged |= LVIF_IMAGE;
2602 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2603 uChanged |= LVIF_PARAM;
2605 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2606 uChanged |= LVIF_INDENT;
2608 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2609 uChanged |= LVIF_TEXT;
2611 if (!uChanged) return TRUE;
2613 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2614 nmlv.iItem = lpLVItem->iItem;
2615 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2616 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2617 nmlv.uChanged = uChanged;
2618 nmlv.lParam = lpItem->lParam;
2620 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2621 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2622 return FALSE;
2624 /* copy information */
2625 if (lpLVItem->mask & LVIF_TEXT)
2626 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2628 if (lpLVItem->mask & LVIF_IMAGE)
2629 lpItem->hdr.iImage = lpLVItem->iImage;
2631 if (lpLVItem->mask & LVIF_PARAM)
2632 lpItem->lParam = lpLVItem->lParam;
2634 if (lpLVItem->mask & LVIF_INDENT)
2635 lpItem->iIndent = lpLVItem->iIndent;
2637 if (uChanged & LVIF_STATE)
2639 lpItem->state &= ~lpLVItem->stateMask;
2640 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2641 if (nmlv.uNewState & LVIS_SELECTED)
2643 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2644 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2646 else if (lpLVItem->stateMask & LVIS_SELECTED)
2647 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2649 /* if we are asked to change focus, and we manage it, do it */
2650 if (nmlv.uNewState & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2652 if (lpLVItem->state & LVIS_FOCUSED)
2654 infoPtr->nFocusedItem = lpLVItem->iItem;
2655 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2657 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2658 infoPtr->nFocusedItem = -1;
2662 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2663 if((uChanged & LVIF_TEXT) && ((uView == LVS_LIST) || (uView == LVS_SMALLICON)))
2665 int item_width = LISTVIEW_CalculateWidth(infoPtr, lpLVItem->iItem);
2666 if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width;
2669 /* if we're inserting the item, we're done */
2670 if (!lpItem->valid) return TRUE;
2672 /* send LVN_ITEMCHANGED notification */
2673 nmlv.lParam = lpItem->lParam;
2674 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2676 return TRUE;
2679 /***
2680 * DESCRIPTION:
2681 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2683 * PARAMETER(S):
2684 * [I] infoPtr : valid pointer to the listview structure
2685 * [I] lpLVItem : valid pointer to new subitem atttributes
2686 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2688 * RETURN:
2689 * SUCCESS : TRUE
2690 * FAILURE : FALSE
2692 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2694 HDPA hdpaSubItems;
2695 LISTVIEW_SUBITEM *lpSubItem;
2697 /* set subitem only if column is present */
2698 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2699 return FALSE;
2701 /* First do some sanity checks */
2702 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2704 /* get the subitem structure, and create it if not there */
2705 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2706 if (!hdpaSubItems) return FALSE;
2708 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2709 if (!lpSubItem)
2711 LISTVIEW_SUBITEM *tmpSubItem;
2712 INT i;
2714 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2715 if (!lpSubItem) return FALSE;
2716 /* we could binary search here, if need be...*/
2717 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2719 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2720 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2722 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2724 COMCTL32_Free(lpSubItem);
2725 return FALSE;
2727 lpSubItem->iSubItem = lpLVItem->iSubItem;
2730 if (lpLVItem->mask & LVIF_IMAGE)
2731 lpSubItem->hdr.iImage = lpLVItem->iImage;
2733 if (lpLVItem->mask & LVIF_TEXT)
2734 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2736 return TRUE;
2739 /***
2740 * DESCRIPTION:
2741 * Sets item attributes.
2743 * PARAMETER(S):
2744 * [I] infoPtr : valid pointer to the listview structure
2745 * [I] LPLVITEM : new item atttributes
2746 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2748 * RETURN:
2749 * SUCCESS : TRUE
2750 * FAILURE : FALSE
2752 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2754 INT oldFocus = infoPtr->nFocusedItem;
2755 LPWSTR pszText = NULL;
2756 BOOL bResult;
2758 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2760 if (!lpLVItem || lpLVItem->iItem < 0 ||
2761 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2762 return FALSE;
2764 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2765 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
2767 pszText = lpLVItem->pszText;
2768 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
2771 /* actually set the fields */
2772 if (infoPtr->dwStyle & LVS_OWNERDATA)
2773 bResult = set_owner_item(infoPtr, lpLVItem, TRUE);
2774 else
2776 /* sanity checks first */
2777 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
2779 if (lpLVItem->iSubItem)
2780 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
2781 else
2782 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
2785 /* redraw item, if necessary */
2786 if (bResult && !infoPtr->bIsDrawing)
2788 if (oldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
2790 LISTVIEW_ToggleFocusRect(infoPtr);
2791 /* Note that ->rcLargeFocus is normally all zero, so
2792 * no second InvalidateRect is issued.
2794 * However, when a large icon style is drawn (LVS_ICON),
2795 * the rectangle drawn is saved in rcLastDraw. That way
2796 * the InvalidateRect will invalidate the entire area drawn
2798 if (!IsRectEmpty(&infoPtr->rcLargeFocus))
2799 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcLargeFocus);
2801 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
2803 /* restore text */
2804 if (pszText)
2806 textfreeT(lpLVItem->pszText, isW);
2807 lpLVItem->pszText = pszText;
2810 return bResult;
2813 /***
2814 * DESCRIPTION:
2815 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2817 * PARAMETER(S):
2818 * [I] infoPtr : valid pointer to the listview structure
2820 * RETURN:
2821 * item index
2823 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2825 LONG lStyle = infoPtr->dwStyle;
2826 UINT uView = lStyle & LVS_TYPEMASK;
2827 INT nItem = 0;
2828 SCROLLINFO scrollInfo;
2830 scrollInfo.cbSize = sizeof(SCROLLINFO);
2831 scrollInfo.fMask = SIF_POS;
2833 if (uView == LVS_LIST)
2835 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2836 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2838 else if (uView == LVS_REPORT)
2840 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2841 nItem = scrollInfo.nPos;
2843 else
2845 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2846 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
2849 TRACE("nItem=%d\n", nItem);
2851 return nItem;
2854 /* used by the drawing code */
2855 typedef struct tagTEXTATTR
2857 int bkMode;
2858 COLORREF bkColor;
2859 COLORREF fgColor;
2860 } TEXTATTR;
2862 /* helper function for the drawing code */
2863 static inline void set_text_attr(HDC hdc, TEXTATTR *ta)
2865 ta->bkMode = SetBkMode(hdc, ta->bkMode);
2866 ta->bkColor = SetBkColor(hdc, ta->bkColor);
2867 ta->fgColor = SetTextColor(hdc, ta->fgColor);
2870 /* helper function for the drawing code */
2871 static void select_text_attr(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL isSelected, TEXTATTR *ta)
2873 ta->bkMode = OPAQUE;
2875 if (isSelected && infoPtr->bFocus)
2877 ta->bkColor = comctl32_color.clrHighlight;
2878 ta->fgColor = comctl32_color.clrHighlightText;
2880 else if (isSelected && (infoPtr->dwStyle & LVS_SHOWSELALWAYS))
2882 ta->bkColor = comctl32_color.clr3dFace;
2883 ta->fgColor = comctl32_color.clrBtnText;
2885 else if ( (infoPtr->clrTextBk != CLR_DEFAULT) && (infoPtr->clrTextBk != CLR_NONE) )
2887 ta->bkColor = infoPtr->clrTextBk;
2888 ta->fgColor = infoPtr->clrText;
2890 else
2892 ta->bkMode = TRANSPARENT;
2893 ta->bkColor = GetBkColor(hdc);
2894 ta->fgColor = infoPtr->clrText;
2897 set_text_attr(hdc, ta);
2900 /***
2901 * DESCRIPTION:
2902 * Erases the background of the given rectangle
2904 * PARAMETER(S):
2905 * [I] infoPtr : valid pointer to the listview structure
2906 * [I] hdc : device context handle
2907 * [I] lprcBox : clipping rectangle
2909 * RETURN:
2910 * Success: TRUE
2911 * Failure: FALSE
2913 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
2915 if (!infoPtr->hBkBrush) return FALSE;
2917 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
2919 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
2922 /***
2923 * DESCRIPTION:
2924 * Draws a subitem.
2926 * PARAMETER(S):
2927 * [I] infoPtr : valid pointer to the listview structure
2928 * [I] HDC : device context handle
2929 * [I] INT : item index
2930 * [I] INT : subitem index
2931 * [I] RECT * : clipping rectangle
2933 * RETURN:
2934 * Success: TRUE
2935 * Failure: FALSE
2937 static BOOL LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem,
2938 INT nSubItem, RECT rcItem, UINT align)
2940 WCHAR szDispText[DISP_TEXT_SIZE];
2941 LVITEMW lvItem;
2943 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, rcItem=%s)\n",
2944 hdc, nItem, nSubItem, debugrect(&rcItem));
2946 /* get information needed for drawing the item */
2947 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
2948 lvItem.iItem = nItem;
2949 lvItem.iSubItem = nSubItem;
2950 lvItem.cchTextMax = COUNTOF(szDispText);
2951 lvItem.pszText = szDispText;
2952 *lvItem.pszText = '\0';
2953 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
2955 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2957 if (lvItem.iImage) FIXME("Draw the image for the subitem\n");
2959 DrawTextW(hdc, lvItem.pszText, -1, &rcItem,
2960 DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | align);
2962 return TRUE;
2966 /***
2967 * DESCRIPTION:
2968 * Draws an item.
2970 * PARAMETER(S):
2971 * [I] infoPtr : valid pointer to the listview structure
2972 * [I] hdc : device context handle
2973 * [I] nItem : item index
2974 * [I] rcItem : item rectangle
2976 * RETURN:
2977 * TRUE: if item is focused
2978 * FALSE: otherwise
2980 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
2982 WCHAR szDispText[DISP_TEXT_SIZE];
2983 INT nLabelWidth, imagePadding = 0;
2984 RECT* lprcFocus, rcOrig = rcItem;
2985 LVITEMW lvItem;
2986 TEXTATTR ta;
2988 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
2990 /* get information needed for drawing the item */
2991 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2992 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
2993 lvItem.iItem = nItem;
2994 lvItem.iSubItem = 0;
2995 lvItem.cchTextMax = DISP_TEXT_SIZE;
2996 lvItem.pszText = szDispText;
2997 *lvItem.pszText = '\0';
2998 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
2999 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3001 /* now check if we need to update the focus rectangle */
3002 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3003 if (lprcFocus) SetRectEmpty(lprcFocus);
3005 /* do indent */
3006 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3008 /* state icons */
3009 if (infoPtr->himlState)
3011 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3012 if (uStateImage)
3014 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3015 rcItem.left, rcItem.top, ILD_NORMAL);
3017 rcItem.left += infoPtr->iconStateSize.cx;
3018 imagePadding = IMAGE_PADDING;
3021 /* small icons */
3022 if (infoPtr->himlSmall)
3024 if (lvItem.iImage >= 0)
3026 UINT mode = (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ?
3027 ILD_SELECTED : ILD_NORMAL;
3028 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3029 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc,
3030 rcItem.left, rcItem.top, mode);
3032 rcItem.left += infoPtr->iconSize.cx;
3033 imagePadding = IMAGE_PADDING;
3036 /* Don't bother painting item being edited */
3037 if (infoPtr->bEditing && lprcFocus)
3038 return FALSE;
3040 select_text_attr(infoPtr, hdc, lvItem.state & LVIS_SELECTED, &ta);
3042 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
3043 rcItem.left += imagePadding;
3044 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3045 if (rcItem.right > rcOrig.right) rcItem.right = rcOrig.right;
3047 if (lvItem.pszText)
3049 TRACE("drawing text=%s, in rect=%s\n", debugstr_w(lvItem.pszText), debugrect(&rcItem));
3050 if(lprcFocus) *lprcFocus = rcItem;
3051 if (lvItem.state & LVIS_SELECTED)
3052 ExtTextOutW(hdc, rcItem.left, rcItem.top, ETO_OPAQUE, &rcItem, 0, 0, 0);
3053 DrawTextW(hdc, lvItem.pszText, -1, &rcItem,
3054 DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | DT_CENTER);
3057 set_text_attr(hdc, &ta);
3058 return lprcFocus != NULL;
3061 /***
3062 * DESCRIPTION:
3063 * Draws an item when in large icon display mode.
3065 * PARAMETER(S):
3066 * [I] infoPtr : valid pointer to the listview structure
3067 * [I] hdc : device context handle
3068 * [I] nItem : item index
3069 * [I] rcItem : clipping rectangle
3071 * RETURN:
3072 * TRUE: if item is focused
3073 * FALSE: otherwise
3075 static BOOL LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
3077 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3078 LVITEMW lvItem;
3079 UINT uFormat = LISTVIEW_DTFLAGS;
3080 RECT rcIcon, rcFocus, rcFullText, rcLabel, *lprcFocus;
3081 POINT ptOrg;
3083 TRACE("(hdc=%x, nItem=%d, rcItem=%s)\n", hdc, nItem, debugrect(&rcItem));
3085 /* get information needed for drawing the item */
3086 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3087 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3088 lvItem.iItem = nItem;
3089 lvItem.iSubItem = 0;
3090 lvItem.cchTextMax = DISP_TEXT_SIZE;
3091 lvItem.pszText = szDispText;
3092 *lvItem.pszText = '\0';
3093 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3094 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3096 /* now check if we need to update the focus rectangle */
3097 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3099 LISTVIEW_GetItemMeasures(infoPtr, nItem, &ptOrg, NULL, &rcIcon, &rcLabel, &rcFullText);
3101 /* Set the item to the boundary box for now */
3102 TRACE("iconSize.cx=%ld, nItemWidth=%d\n", infoPtr->iconSize.cx, infoPtr->nItemWidth);
3103 TRACE("rcList=%s, rcView=%s\n", debugrect(&infoPtr->rcList), debugrect(&infoPtr->rcView));
3105 /* Figure out text colours etc. depending on state
3106 * At least the following states exist; there may be more.
3107 * Many items may be selected
3108 * At most one item may have the focus
3109 * The application may not actually be active currently
3110 * 1. The item is not selected in any way
3111 * 2. The cursor is flying over the icon or text and the text is being
3112 * expanded because it is not fully displayed currently.
3113 * 3. The item is selected and is focussed, i.e. the user has not clicked
3114 * in the blank area of the window, and the window (or application?)
3115 * still has the focus.
3116 * 4. As 3 except that a different window has the focus
3117 * 5. The item is the selected item of all the items, but the user has
3118 * clicked somewhere else on the window.
3119 * Only a few of these are handled currently. In particular 2 is not yet
3120 * handled since we do not support the functionality currently (or at least
3121 * we didn't when I wrote this)
3124 if (lvItem.state & LVIS_SELECTED)
3126 /* set item colors */
3127 SetBkColor(hdc, comctl32_color.clrHighlight);
3128 SetTextColor(hdc, comctl32_color.clrHighlightText);
3129 SetBkMode (hdc, OPAQUE);
3130 /* set raster mode */
3131 SetROP2(hdc, R2_XORPEN);
3132 /* When exactly is it in XOR? while being dragged? */
3134 else
3136 /* set item colors */
3137 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3139 SetBkMode(hdc, TRANSPARENT);
3141 else
3143 SetBkMode(hdc, OPAQUE);
3144 SetBkColor(hdc, infoPtr->clrTextBk);
3147 SetTextColor(hdc, infoPtr->clrText);
3148 /* set raster mode */
3149 SetROP2(hdc, R2_COPYPEN);
3152 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3153 * wrapping and long words split.
3154 * In cases 1 and 4 only a portion of the text is displayed with word
3155 * wrapping and both word and end ellipsis. (I don't yet know about path
3156 * ellipsis)
3158 uFormat |= lprcFocus ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3160 /* state icons */
3161 if (infoPtr->himlState != NULL)
3163 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3164 INT x, y;
3166 x = rcIcon.left - infoPtr->iconStateSize.cx + 10;
3167 y = rcIcon.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
3168 if (uStateImage > 0)
3170 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, x,
3171 y, ILD_NORMAL);
3175 /* draw the icon */
3176 if (infoPtr->himlNormal != NULL)
3178 if (lvItem.iImage >= 0)
3180 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc,
3181 rcIcon.left+ICON_LR_HALF,
3182 rcIcon.top+ICON_TOP_PADDING_HITABLE,
3183 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3184 TRACE("icon %d at (%d,%d)\n",
3185 lvItem.iImage, rcIcon.left+ICON_LR_HALF,
3186 rcIcon.top+ICON_TOP_PADDING_HITABLE);
3190 /* Draw the text below the icon */
3192 /* Don't bother painting item being edited */
3193 if ((infoPtr->bEditing && lprcFocus) || !lvItem.pszText || !lstrlenW(lvItem.pszText))
3195 if(lprcFocus) SetRectEmpty(lprcFocus);
3196 return FALSE;
3199 /* draw label */
3201 /* I am sure of most of the uFormat values. However I am not sure about
3202 * whether we need or do not need the following:
3203 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3204 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3205 * We certainly do not need
3206 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3207 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3210 /* If the text is being drawn without clipping (i.e. the full text) then we
3211 * need to jump through a few hoops to ensure that it all gets displayed and
3212 * that the background is complete
3214 rcFocus = rcLabel; /* save for focus */
3215 SetRectEmpty(&infoPtr->rcLargeFocus);
3216 if ((uFormat & DT_NOCLIP) || (lvItem.state & LVIS_SELECTED))
3218 /* FIXME: why do we need this??? */
3219 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3221 FillRect(hdc, &rcFullText, hBrush);
3222 rcFocus = rcFullText;
3223 DeleteObject(hBrush);
3225 /* Save size of item drawing for next InvalidateRect */
3226 infoPtr->rcLargeFocus = rcFullText;
3227 TRACE("focused/selected, rcFocus=%s\n", debugrect(&rcFocus));
3229 /* else ? What if we are losing the focus? will we not get a complete
3230 * background?
3233 DrawTextW (hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3234 TRACE("text at rcLabel=%s is %s\n", debugrect(&rcLabel), debugstr_w(lvItem.pszText));
3236 if(lprcFocus) CopyRect(lprcFocus, &rcFocus);
3238 return lprcFocus != NULL;
3241 /***
3242 * DESCRIPTION:
3243 * Draws listview items when in report display mode.
3245 * PARAMETER(S):
3246 * [I] infoPtr : valid pointer to the listview structure
3247 * [I] HDC : device context handle
3249 * RETURN:
3250 * None
3252 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3254 INT rgntype, nDrawPosY, j;
3255 INT nTop, nItem, nLast, nUpdateHeight, nUpdateWidth;
3256 INT nColumnCount, nFirstCol, nLastCol;
3257 RECT rcItem, rcClip, rcFullSelect;
3258 BOOL bFullSelected, isFocused;
3259 DWORD cditemmode = CDRF_DODEFAULT;
3260 LONG lStyle = infoPtr->dwStyle;
3261 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3262 TEXTATTR tmpTa, oldTa;
3263 COLUMNCACHE *lpCols;
3264 LVCOLUMNW lvColumn;
3265 LVITEMW item;
3266 POINT ptOrig;
3268 TRACE("()\n");
3270 /* nothing to draw */
3271 if(GETITEMCOUNT(infoPtr) == 0) return;
3273 /* figure out what to draw */
3274 rgntype = GetClipBox(hdc, &rcClip);
3275 if (rgntype == NULLREGION) return;
3276 nUpdateHeight = rcClip.bottom - rcClip.top + 1;
3277 nUpdateWidth = rcClip.right - rcClip.left;
3278 nTop = LISTVIEW_GetTopIndex(infoPtr);
3279 nItem = nTop + (rcClip.top - infoPtr->rcList.top) / infoPtr->nItemHeight;
3280 if (nItem < nTop)
3281 nItem = nTop;
3282 nLast = nItem + nUpdateHeight / infoPtr->nItemHeight;
3283 if (nUpdateHeight % infoPtr->nItemHeight) nLast++;
3284 if (nLast > GETITEMCOUNT(infoPtr))
3285 nLast = GETITEMCOUNT(infoPtr);
3287 /* send cache hint notification */
3288 if (lStyle & LVS_OWNERDATA)
3289 notify_odcachehint(infoPtr, nItem, nLast);
3291 /* cache column info */
3292 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3293 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3294 if (!lpCols) return;
3295 for (j = 0; j < nColumnCount; j++)
3297 Header_GetItemRect(infoPtr->hwndHeader, j, &lpCols[j].rc);
3298 TRACE("lpCols[%d].rc=%s\n", j, debugrect(&lpCols[j].rc));
3301 /* Get scroll info once before loop */
3302 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrig)) return;
3304 /* we now narrow the columns as well */
3305 nLastCol = nColumnCount - 1;
3306 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3307 if (lpCols[nFirstCol].rc.right + ptOrig.x >= rcClip.left) break;
3308 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3309 if (lpCols[nLastCol].rc.left + ptOrig.x < rcClip.right) break;
3311 /* cache the per-column information before we start drawing */
3312 for (j = nFirstCol; j <= nLastCol; j++)
3314 lvColumn.mask = LVCF_FMT;
3315 LISTVIEW_GetColumnT(infoPtr, j, &lvColumn, TRUE);
3316 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3317 lpCols[j].align = DT_LEFT;
3318 if (lvColumn.fmt & LVCFMT_RIGHT)
3319 lpCols[j].align = DT_RIGHT;
3320 else if (lvColumn.fmt & LVCFMT_CENTER)
3321 lpCols[j].align = DT_CENTER;
3324 /* a last few bits before we start drawing */
3325 TRACE("nTop=%d, nItem=%d, nLast=%d, nFirstCol=%d, nLastCol=%d\n",
3326 nTop, nItem, nLast, nFirstCol, nLastCol);
3327 bFullSelected = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT;
3328 nDrawPosY = infoPtr->rcList.top + (nItem - nTop) * infoPtr->nItemHeight;
3330 /* save dc values we're gonna trash while drawing */
3331 oldTa.bkMode = GetBkMode(hdc);
3332 oldTa.bkColor = GetBkColor(hdc);
3333 oldTa.fgColor = GetTextColor(hdc);
3335 /* iterate through the invalidated rows */
3336 for (; nItem < nLast; nItem++, nDrawPosY += infoPtr->nItemHeight)
3338 /* if owner wants to take a first stab at it, have it his way... */
3339 if (lStyle & LVS_OWNERDRAWFIXED)
3341 DRAWITEMSTRUCT dis;
3343 TRACE("Owner Drawn\n");
3345 item.iItem = nItem;
3346 item.iSubItem = 0;
3347 item.mask = LVIF_PARAM | LVIF_STATE;
3348 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3349 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3351 ZeroMemory(&dis, sizeof(dis));
3352 dis.CtlType = ODT_LISTVIEW;
3353 dis.CtlID = uID;
3354 dis.itemID = nItem;
3355 dis.itemAction = ODA_DRAWENTIRE;
3356 if (item.state & LVIS_SELECTED) dis.itemAction |= ODA_SELECT;
3357 if (item.state & LVIS_FOCUSED) dis.itemAction |= ODA_FOCUS;
3358 /*dis.itemState = ODS_DEFAULT; */
3359 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3360 if (item.state & LVIS_FOCUSED) dis.itemState |= ODS_FOCUS;
3361 dis.hwndItem = infoPtr->hwndSelf;
3362 dis.hDC = hdc;
3363 dis.rcItem.left = lpCols[0].rc.left;
3364 dis.rcItem.right = lpCols[nColumnCount - 1].rc.right;
3365 dis.rcItem.top = nDrawPosY;
3366 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3367 OffsetRect(&dis.rcItem, ptOrig.x, 0);
3368 dis.itemData = item.lParam;
3370 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3371 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3372 /* In theory we should do the default drawing if WM_DRAWITEM
3373 * returns FALSE but, in the words of Larry McVoy, in practice
3374 * theory is different than practice, and hence there are
3375 * important apps out there that depend on no default drawing
3376 * in LVS_OWNERDRAWFIXED. So we always skip to the next item. */
3377 continue;
3380 /* compute the full select rectangle, if needed */
3381 if (bFullSelected)
3383 item.mask = LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3384 item.stateMask = LVIS_SELECTED;
3385 item.iItem = nItem;
3386 item.iSubItem = 0;
3387 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3389 rcFullSelect.left = lpCols[0].rc.left + REPORT_MARGINX +
3390 infoPtr->iconSize.cx * item.iIndent +
3391 (infoPtr->himlSmall ? infoPtr->iconSize.cx : 0);
3392 rcFullSelect.right = max(rcFullSelect.left, lpCols[nColumnCount - 1].rc.right - REPORT_MARGINX);
3393 rcFullSelect.top = nDrawPosY;
3394 rcFullSelect.bottom = rcFullSelect.top + infoPtr->nItemHeight;
3395 OffsetRect(&rcFullSelect, ptOrig.x, 0);
3398 /* draw the background of the selection rectangle, if need be */
3399 select_text_attr(infoPtr, hdc, bFullSelected && (item.state & LVIS_SELECTED), &tmpTa);
3400 if (bFullSelected && (item.state & LVIS_SELECTED))
3401 ExtTextOutW(hdc, rcFullSelect.left, rcFullSelect.top, ETO_OPAQUE, &rcFullSelect, 0, 0, 0);
3403 /* iterate through the invalidated columns */
3404 isFocused = FALSE;
3405 for (j = nFirstCol; j <= nLastCol; j++)
3407 if (cdmode & CDRF_NOTIFYITEMDRAW)
3408 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, j, CDDS_ITEMPREPAINT);
3409 if (cditemmode & CDRF_SKIPDEFAULT) continue;
3411 rcItem = lpCols[j].rc;
3412 rcItem.left += REPORT_MARGINX;
3413 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3414 rcItem.top = nDrawPosY;
3415 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3417 /* Offset the Scroll Bar Pos */
3418 OffsetRect(&rcItem, ptOrig.x, 0);
3420 if (j == 0)
3421 isFocused = LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3422 else
3423 LISTVIEW_DrawSubItem(infoPtr, hdc, nItem, j, rcItem, lpCols[j].align);
3425 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3426 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3429 /* Adjust focus if we have it, and we are in full select */
3430 if (bFullSelected && isFocused) infoPtr->rcFocus = rcFullSelect;
3433 /* cleanup the mess */
3434 set_text_attr(hdc, &oldTa);
3435 COMCTL32_Free(lpCols);
3438 /***
3439 * DESCRIPTION:
3440 * Draws listview items when in list display mode.
3442 * PARAMETER(S):
3443 * [I] infoPtr : valid pointer to the listview structure
3444 * [I] HDC : device context handle
3446 * RETURN:
3447 * None
3449 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3451 RECT rcItem;
3452 INT i, j;
3453 INT nItem;
3454 INT nColumnCount;
3455 INT nCountPerColumn;
3456 INT nItemWidth = infoPtr->nItemWidth;
3457 INT nItemHeight = infoPtr->nItemHeight;
3458 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3459 DWORD cditemmode = CDRF_DODEFAULT;
3461 /* get number of fully visible columns */
3462 nColumnCount = nListWidth / nItemWidth;
3463 if (nListWidth % nItemWidth) nColumnCount++;
3464 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
3465 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3466 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3467 nColumnCount, nCountPerColumn, nItem);
3469 /* nothing to draw, return here */
3470 if(GETITEMCOUNT(infoPtr) == 0)
3471 return;
3473 for (i = 0; i < nColumnCount; i++)
3475 for (j = 0; j < nCountPerColumn; j++, nItem++)
3477 if (nItem >= GETITEMCOUNT(infoPtr))
3478 return;
3480 if (cdmode & CDRF_NOTIFYITEMDRAW)
3481 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, 0, CDDS_ITEMPREPAINT);
3482 if (cditemmode & CDRF_SKIPDEFAULT)
3483 continue;
3485 rcItem.top = j * nItemHeight;
3486 rcItem.left = i * nItemWidth;
3487 rcItem.bottom = rcItem.top + nItemHeight;
3488 rcItem.right = rcItem.left + nItemWidth;
3490 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3492 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3493 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3499 /***
3500 * DESCRIPTION:
3501 * Draws listview items when in icon or small icon display mode.
3503 * PARAMETER(S):
3504 * [I] infoPtr : valid pointer to the listview structure
3505 * [I] HDC : device context handle
3507 * RETURN:
3508 * None
3510 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL bSmall, DWORD cdmode)
3512 POINT ptPosition;
3513 POINT ptOrigin;
3514 RECT rcItem, rcClip, rcTemp;
3515 INT i;
3516 DWORD cditemmode = CDRF_DODEFAULT;
3518 TRACE("\n");
3520 /* nothing to draw, return here */
3521 if(GETITEMCOUNT(infoPtr) == 0)
3522 return;
3524 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return;
3526 GetClipBox(hdc, &rcClip);
3528 /* Draw the visible non-selected items */
3529 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3531 if (LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3532 continue;
3534 rcItem.left = LVIR_BOUNDS;
3535 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3536 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3537 continue;
3539 if (cdmode & CDRF_NOTIFYITEMDRAW)
3540 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3541 if (cditemmode & CDRF_SKIPDEFAULT)
3542 continue;
3544 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3545 ptPosition.x += ptOrigin.x;
3546 ptPosition.y += ptOrigin.y;
3548 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3550 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3552 if (ptPosition.y < infoPtr->rcList.bottom)
3554 if (ptPosition.x < infoPtr->rcList.right)
3556 rcItem.top = ptPosition.y;
3557 rcItem.left = ptPosition.x;
3558 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3559 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3561 if (bSmall)
3562 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3563 else
3564 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3569 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3570 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3573 /* Draw the visible selected items */
3574 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3576 if (!LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3577 continue;
3579 rcItem.left = LVIR_BOUNDS;
3580 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3581 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3582 continue;
3584 if (cdmode & CDRF_NOTIFYITEMDRAW)
3585 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3586 if (cditemmode & CDRF_SKIPDEFAULT)
3587 continue;
3589 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3590 ptPosition.x += ptOrigin.x;
3591 ptPosition.y += ptOrigin.y;
3593 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3595 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3597 if (ptPosition.y < infoPtr->rcList.bottom)
3599 if (ptPosition.x < infoPtr->rcList.right)
3601 rcItem.top = ptPosition.y;
3602 rcItem.left = ptPosition.x;
3603 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3604 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3606 if (bSmall)
3607 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3608 else
3609 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3614 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3615 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3619 /***
3620 * DESCRIPTION:
3621 * Draws listview items.
3623 * PARAMETER(S):
3624 * [I] infoPtr : valid pointer to the listview structure
3625 * [I] HDC : device context handle
3627 * RETURN:
3628 * NoneX
3630 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3632 UINT uView = LISTVIEW_GetType(infoPtr);
3633 HFONT hOldFont;
3634 DWORD cdmode;
3635 RECT rcClient;
3637 LISTVIEW_DUMP(infoPtr);
3639 GetClientRect(infoPtr->hwndSelf, &rcClient);
3641 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, hdc, rcClient);
3642 if (cdmode == CDRF_SKIPDEFAULT) return;
3644 infoPtr->bIsDrawing = TRUE;
3646 /* select font */
3647 hOldFont = SelectObject(hdc, infoPtr->hFont);
3649 if (uView == LVS_LIST)
3650 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3651 else if (uView == LVS_REPORT)
3652 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3653 else
3654 LISTVIEW_RefreshIcon(infoPtr, hdc, uView == LVS_SMALLICON, cdmode);
3656 /* if we have a focus rect, draw it */
3657 if (infoPtr->bFocus && !IsRectEmpty(&infoPtr->rcFocus))
3658 DrawFocusRect(hdc, &infoPtr->rcFocus);
3660 /* unselect objects */
3661 SelectObject(hdc, hOldFont);
3663 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3664 notify_customdraw(infoPtr, CDDS_POSTPAINT, hdc, rcClient);
3666 infoPtr->bIsDrawing = FALSE;
3670 /***
3671 * DESCRIPTION:
3672 * Calculates the approximate width and height of a given number of items.
3674 * PARAMETER(S):
3675 * [I] infoPtr : valid pointer to the listview structure
3676 * [I] INT : number of items
3677 * [I] INT : width
3678 * [I] INT : height
3680 * RETURN:
3681 * Returns a DWORD. The width in the low word and the height in high word.
3683 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3684 WORD wWidth, WORD wHeight)
3686 UINT uView = LISTVIEW_GetType(infoPtr);
3687 INT nItemCountPerColumn = 1;
3688 INT nColumnCount = 0;
3689 DWORD dwViewRect = 0;
3691 if (nItemCount == -1)
3692 nItemCount = GETITEMCOUNT(infoPtr);
3694 if (uView == LVS_LIST)
3696 if (wHeight == 0xFFFF)
3698 /* use current height */
3699 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3702 if (wHeight < infoPtr->nItemHeight)
3703 wHeight = infoPtr->nItemHeight;
3705 if (nItemCount > 0)
3707 if (infoPtr->nItemHeight > 0)
3709 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3710 if (nItemCountPerColumn == 0)
3711 nItemCountPerColumn = 1;
3713 if (nItemCount % nItemCountPerColumn != 0)
3714 nColumnCount = nItemCount / nItemCountPerColumn;
3715 else
3716 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3720 /* Microsoft padding magic */
3721 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3722 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3724 dwViewRect = MAKELONG(wWidth, wHeight);
3726 else if (uView == LVS_REPORT)
3727 FIXME("uView == LVS_REPORT: not implemented\n");
3728 else if (uView == LVS_SMALLICON)
3729 FIXME("uView == LVS_SMALLICON: not implemented\n");
3730 else if (uView == LVS_ICON)
3731 FIXME("uView == LVS_ICON: not implemented\n");
3733 return dwViewRect;
3736 /***
3737 * DESCRIPTION:
3738 * Arranges listview items in icon display mode.
3740 * PARAMETER(S):
3741 * [I] infoPtr : valid pointer to the listview structure
3742 * [I] INT : alignment code
3744 * RETURN:
3745 * SUCCESS : TRUE
3746 * FAILURE : FALSE
3748 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3750 UINT uView = LISTVIEW_GetType(infoPtr);
3751 BOOL bResult = FALSE;
3753 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3755 switch (nAlignCode)
3757 case LVA_ALIGNLEFT:
3758 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3759 break;
3760 case LVA_ALIGNTOP:
3761 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3762 break;
3763 case LVA_DEFAULT:
3764 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3765 break;
3766 case LVA_SNAPTOGRID:
3767 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3768 break;
3772 return bResult;
3775 /* << LISTVIEW_CreateDragImage >> */
3778 /***
3779 * DESCRIPTION:
3780 * Removes all listview items and subitems.
3782 * PARAMETER(S):
3783 * [I] infoPtr : valid pointer to the listview structure
3785 * RETURN:
3786 * SUCCESS : TRUE
3787 * FAILURE : FALSE
3789 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3791 LONG lStyle = infoPtr->dwStyle;
3792 UINT uView = lStyle & LVS_TYPEMASK;
3793 LISTVIEW_ITEM *lpItem;
3794 LISTVIEW_SUBITEM *lpSubItem;
3795 NMLISTVIEW nmlv;
3796 BOOL bSuppress;
3797 BOOL bResult = FALSE;
3798 HDPA hdpaSubItems;
3800 TRACE("()\n");
3802 LISTVIEW_RemoveAllSelections(infoPtr);
3803 infoPtr->nSelectionMark=-1;
3804 infoPtr->nFocusedItem=-1;
3805 /* But we are supposed to leave nHotItem as is! */
3807 if (lStyle & LVS_OWNERDATA)
3809 infoPtr->hdpaItems->nItemCount = 0;
3810 LISTVIEW_InvalidateList(infoPtr);
3811 return TRUE;
3814 if (GETITEMCOUNT(infoPtr) > 0)
3816 INT i, j;
3818 /* send LVN_DELETEALLITEMS notification */
3819 /* verify if subsequent LVN_DELETEITEM notifications should be
3820 suppressed */
3821 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3822 nmlv.iItem = -1;
3823 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3825 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3827 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3828 if (hdpaSubItems != NULL)
3830 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3832 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3833 if (lpSubItem != NULL)
3835 /* free subitem string */
3836 if (is_textW(lpSubItem->hdr.pszText))
3837 COMCTL32_Free(lpSubItem->hdr.pszText);
3839 /* free subitem */
3840 COMCTL32_Free(lpSubItem);
3844 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3845 if (lpItem != NULL)
3847 if (!bSuppress)
3849 /* send LVN_DELETEITEM notification */
3850 nmlv.iItem = i;
3851 nmlv.lParam = lpItem->lParam;
3852 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3855 /* free item string */
3856 if (is_textW(lpItem->hdr.pszText))
3857 COMCTL32_Free(lpItem->hdr.pszText);
3859 /* free item */
3860 COMCTL32_Free(lpItem);
3863 DPA_Destroy(hdpaSubItems);
3867 /* reinitialize listview memory */
3868 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3870 /* align items (set position of each item) */
3871 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3873 if (lStyle & LVS_ALIGNLEFT)
3875 LISTVIEW_AlignLeft(infoPtr);
3877 else
3879 LISTVIEW_AlignTop(infoPtr);
3883 LISTVIEW_UpdateScroll(infoPtr);
3885 LISTVIEW_InvalidateList(infoPtr);
3888 return bResult;
3891 /***
3892 * DESCRIPTION:
3893 * Removes a column from the listview control.
3895 * PARAMETER(S):
3896 * [I] infoPtr : valid pointer to the listview structure
3897 * [I] INT : column index
3899 * RETURN:
3900 * SUCCESS : TRUE
3901 * FAILURE : FALSE
3903 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3905 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3906 RECT rcCol, rcOld;
3908 TRACE("nColumn=%d\n", nColumn);
3910 if (nColumn <= 0) return FALSE;
3912 if (uView == LVS_REPORT)
3914 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3915 return FALSE;
3917 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3918 return FALSE;
3921 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3923 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3924 HDPA hdpaSubItems;
3925 INT nItem, nSubItem, i;
3927 for (nItem = 0; nItem < infoPtr->hdpaItems->nItemCount; nItem++)
3929 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3930 if (!hdpaSubItems) continue;
3931 nSubItem = 0;
3932 lpDelItem = 0;
3933 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3935 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3936 if (!lpSubItem) break;
3937 if (lpSubItem->iSubItem == nColumn)
3939 nSubItem = i;
3940 lpDelItem = lpSubItem;
3942 else if (lpSubItem->iSubItem > nColumn)
3944 lpSubItem->iSubItem--;
3948 /* if we found our subitem, zapp it */
3949 if (nSubItem > 0)
3951 /* free string */
3952 if (is_textW(lpDelItem->hdr.pszText))
3953 COMCTL32_Free(lpDelItem->hdr.pszText);
3955 /* free item */
3956 COMCTL32_Free(lpDelItem);
3958 /* free dpa memory */
3959 DPA_DeletePtr(hdpaSubItems, nSubItem);
3964 /* we need to worry about display issues in report mode only */
3965 if (uView != LVS_REPORT) return TRUE;
3967 /* Need to reset the item width when deleting a column */
3968 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
3970 /* update scrollbar(s) */
3971 LISTVIEW_UpdateScroll(infoPtr);
3973 /* scroll to cover the deleted column, and invalidate for redraw */
3974 rcOld = infoPtr->rcList;
3975 rcOld.left = rcCol.left;
3976 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
3977 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3979 return TRUE;
3982 /***
3983 * DESCRIPTION:
3984 * Removes an item from the listview control.
3986 * PARAMETER(S):
3987 * [I] infoPtr : valid pointer to the listview structure
3988 * [I] INT : item index
3990 * RETURN:
3991 * SUCCESS : TRUE
3992 * FAILURE : FALSE
3994 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3996 LONG lStyle = infoPtr->dwStyle;
3997 UINT uView = lStyle & LVS_TYPEMASK;
3998 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3999 NMLISTVIEW nmlv;
4000 BOOL bResult = FALSE;
4001 HDPA hdpaSubItems;
4002 LISTVIEW_ITEM *lpItem;
4003 LISTVIEW_SUBITEM *lpSubItem;
4004 INT i;
4005 LVITEMW item;
4007 TRACE("(nItem=%d)\n", nItem);
4010 /* First, send LVN_DELETEITEM notification. */
4011 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4012 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
4013 nmlv.hdr.idFrom = lCtrlId;
4014 nmlv.hdr.code = LVN_DELETEITEM;
4015 nmlv.iItem = nItem;
4016 SendMessageW((infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
4017 (LPARAM)&nmlv);
4020 /* remove it from the selection range */
4021 item.state = LVIS_SELECTED;
4022 item.stateMask = LVIS_SELECTED;
4023 LISTVIEW_SetItemState(infoPtr,nItem,&item);
4025 if (lStyle & LVS_OWNERDATA)
4027 infoPtr->hdpaItems->nItemCount --;
4028 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
4029 return TRUE;
4032 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4034 /* initialize memory */
4035 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4037 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4038 if (hdpaSubItems != NULL)
4040 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4042 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4043 if (lpSubItem != NULL)
4045 /* free item string */
4046 if (is_textW(lpSubItem->hdr.pszText))
4047 COMCTL32_Free(lpSubItem->hdr.pszText);
4049 /* free item */
4050 COMCTL32_Free(lpSubItem);
4054 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4055 if (lpItem != NULL)
4057 /* free item string */
4058 if (is_textW(lpItem->hdr.pszText))
4059 COMCTL32_Free(lpItem->hdr.pszText);
4061 /* free item */
4062 COMCTL32_Free(lpItem);
4065 bResult = DPA_Destroy(hdpaSubItems);
4068 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
4070 /* align items (set position of each item) */
4071 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4073 if (lStyle & LVS_ALIGNLEFT)
4074 LISTVIEW_AlignLeft(infoPtr);
4075 else
4076 LISTVIEW_AlignTop(infoPtr);
4079 LISTVIEW_UpdateScroll(infoPtr);
4081 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
4084 return bResult;
4088 /***
4089 * DESCRIPTION:
4090 * Callback implementation for editlabel control
4092 * PARAMETER(S):
4093 * [I] infoPtr : valid pointer to the listview structure
4094 * [I] pszText : modified text
4095 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4097 * RETURN:
4098 * SUCCESS : TRUE
4099 * FAILURE : FALSE
4101 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4103 NMLVDISPINFOW dispInfo;
4105 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4107 infoPtr->bEditing = FALSE;
4109 ZeroMemory(&dispInfo, sizeof(dispInfo));
4110 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4111 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4112 dispInfo.item.iSubItem = 0;
4113 dispInfo.item.stateMask = ~0;
4114 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4115 dispInfo.item.pszText = pszText;
4116 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4118 /* Do we need to update the Item Text */
4119 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4120 if (!pszText) return TRUE;
4122 ZeroMemory(&dispInfo, sizeof(dispInfo));
4123 dispInfo.item.mask = LVIF_TEXT;
4124 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4125 dispInfo.item.iSubItem = 0;
4126 dispInfo.item.pszText = pszText;
4127 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4128 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4131 /***
4132 * DESCRIPTION:
4133 * Begin in place editing of specified list view item
4135 * PARAMETER(S):
4136 * [I] infoPtr : valid pointer to the listview structure
4137 * [I] INT : item index
4138 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4140 * RETURN:
4141 * SUCCESS : TRUE
4142 * FAILURE : FALSE
4144 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4146 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4147 NMLVDISPINFOW dispInfo;
4148 RECT rect;
4150 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4152 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4154 infoPtr->nEditLabelItem = nItem;
4156 /* Is the EditBox still there, if so remove it */
4157 if(infoPtr->hwndEdit != 0)
4159 SetFocus(infoPtr->hwndSelf);
4160 infoPtr->hwndEdit = 0;
4163 LISTVIEW_SetSelection(infoPtr, nItem);
4164 LISTVIEW_SetItemFocus(infoPtr, nItem);
4166 rect.left = LVIR_LABEL;
4167 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4169 ZeroMemory(&dispInfo, sizeof(dispInfo));
4170 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4171 dispInfo.item.iItem = nItem;
4172 dispInfo.item.iSubItem = 0;
4173 dispInfo.item.stateMask = ~0;
4174 dispInfo.item.pszText = szDispText;
4175 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4176 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4178 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4179 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4180 if (!infoPtr->hwndEdit) return 0;
4182 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4184 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4185 infoPtr->hwndEdit = 0;
4186 return 0;
4189 infoPtr->bEditing = TRUE;
4190 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4191 SetFocus(infoPtr->hwndEdit);
4192 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4193 return infoPtr->hwndEdit;
4197 /***
4198 * DESCRIPTION:
4199 * Ensures the specified item is visible, scrolling into view if necessary.
4201 * PARAMETER(S):
4202 * [I] infoPtr : valid pointer to the listview structure
4203 * [I] nItem : item index
4204 * [I] bPartial : partially or entirely visible
4206 * RETURN:
4207 * SUCCESS : TRUE
4208 * FAILURE : FALSE
4210 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4212 UINT uView = LISTVIEW_GetType(infoPtr);
4213 INT nScrollPosHeight = 0;
4214 INT nScrollPosWidth = 0;
4215 INT nPartialAdjust = 0;
4216 INT nHorzDiff = 0;
4217 INT nVertDiff = 0;
4218 RECT rcItem;
4220 /* FIXME: ALWAYS bPartial == FALSE, FOR NOW! */
4222 rcItem.left = LVIR_BOUNDS;
4223 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4225 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4227 /* scroll left/right, but in LVS_REPORT mode */
4228 if (uView == LVS_LIST)
4229 nScrollPosWidth = infoPtr->nItemWidth;
4230 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4231 nScrollPosWidth = 1;
4233 if (rcItem.left < infoPtr->rcList.left)
4235 nPartialAdjust = -1;
4236 if (uView != LVS_REPORT) nHorzDiff = rcItem.left + infoPtr->rcList.left;
4238 else
4240 nPartialAdjust = 1;
4241 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4245 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4247 /* scroll up/down, but not in LVS_LIST mode */
4248 if (uView == LVS_REPORT)
4249 nScrollPosHeight = infoPtr->nItemHeight;
4250 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4251 nScrollPosHeight = 1;
4253 if (rcItem.top < infoPtr->rcList.top)
4255 nPartialAdjust = -1;
4256 if (uView != LVS_LIST) nVertDiff = rcItem.top + infoPtr->rcList.top;
4258 else
4260 nPartialAdjust = 1;
4261 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4265 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4267 if (nScrollPosWidth)
4269 INT diff = nHorzDiff / nScrollPosWidth;
4270 if (rcItem.left % nScrollPosWidth) diff += nPartialAdjust;
4271 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4274 if (nScrollPosHeight)
4276 INT diff = nVertDiff / nScrollPosHeight;
4277 if (rcItem.top % nScrollPosHeight) diff += nPartialAdjust;
4278 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4281 return TRUE;
4284 /***
4285 * DESCRIPTION:
4286 * Retrieves the nearest item, given a position and a direction.
4288 * PARAMETER(S):
4289 * [I] infoPtr : valid pointer to the listview structure
4290 * [I] POINT : start position
4291 * [I] UINT : direction
4293 * RETURN:
4294 * Item index if successdful, -1 otherwise.
4296 static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDirection)
4298 LVHITTESTINFO ht;
4299 RECT rcView;
4301 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4302 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4303 ((vkDirection == VK_UP) ? "VK_UP" :
4304 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4306 if (!LISTVIEW_GetViewRect(infoPtr, &rcView)) return -1;
4308 if (!LISTVIEW_GetOrigin(infoPtr, &ht.pt)) return -1;
4310 ht.pt.x += pt.x;
4311 ht.pt.y += pt.y;
4313 if (vkDirection == VK_DOWN) ht.pt.y += infoPtr->nItemHeight;
4314 else if (vkDirection == VK_UP) ht.pt.y -= infoPtr->nItemHeight;
4315 else if (vkDirection == VK_LEFT) ht.pt.x -= infoPtr->nItemWidth;
4316 else if (vkDirection == VK_RIGHT) ht.pt.x += infoPtr->nItemWidth;
4318 if (!PtInRect(&rcView, ht.pt)) return -1;
4320 return LISTVIEW_SuperHitTestItem(infoPtr, &ht, TRUE, TRUE);
4323 /***
4324 * DESCRIPTION:
4325 * Searches for an item with specific characteristics.
4327 * PARAMETER(S):
4328 * [I] hwnd : window handle
4329 * [I] nStart : base item index
4330 * [I] lpFindInfo : item information to look for
4332 * RETURN:
4333 * SUCCESS : index of item
4334 * FAILURE : -1
4336 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4337 LPLVFINDINFOW lpFindInfo)
4339 POINT ptItem;
4340 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4341 LVITEMW lvItem;
4342 BOOL bWrap = FALSE;
4343 INT nItem = nStart;
4344 INT nLast = GETITEMCOUNT(infoPtr);
4346 if ((nItem >= -1) && (lpFindInfo != NULL))
4348 lvItem.mask = 0;
4349 if (lpFindInfo->flags & LVFI_PARAM)
4351 lvItem.mask |= LVIF_PARAM;
4354 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4356 lvItem.mask |= LVIF_TEXT;
4357 lvItem.pszText = szDispText;
4358 lvItem.cchTextMax = DISP_TEXT_SIZE;
4361 if (lpFindInfo->flags & LVFI_WRAP)
4362 bWrap = TRUE;
4364 if (lpFindInfo->flags & LVFI_NEARESTXY)
4366 ptItem.x = lpFindInfo->pt.x;
4367 ptItem.y = lpFindInfo->pt.y;
4370 while (1)
4372 while (nItem < nLast)
4374 if (lpFindInfo->flags & LVFI_NEARESTXY)
4376 nItem = LISTVIEW_GetNearestItem(infoPtr, ptItem,
4377 lpFindInfo->vkDirection);
4378 if (nItem != -1)
4380 /* get position of the new item index */
4381 if (!ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &ptItem))
4382 return -1;
4384 else
4385 return -1;
4387 else
4389 nItem++;
4392 lvItem.iItem = nItem;
4393 lvItem.iSubItem = 0;
4394 if (LISTVIEW_GetItemW(infoPtr, &lvItem))
4396 if (lvItem.mask & LVIF_TEXT)
4398 if (lpFindInfo->flags & LVFI_PARTIAL)
4400 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4401 continue;
4403 else
4405 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4406 continue;
4410 if (lvItem.mask & LVIF_PARAM)
4412 if (lpFindInfo->lParam != lvItem.lParam)
4413 continue;
4416 return nItem;
4420 if (bWrap)
4422 nItem = -1;
4423 nLast = nStart + 1;
4424 bWrap = FALSE;
4426 else
4428 return -1;
4433 return -1;
4436 /***
4437 * DESCRIPTION:
4438 * Searches for an item with specific characteristics.
4440 * PARAMETER(S):
4441 * [I] hwnd : window handle
4442 * [I] nStart : base item index
4443 * [I] lpFindInfo : item information to look for
4445 * RETURN:
4446 * SUCCESS : index of item
4447 * FAILURE : -1
4449 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4450 LPLVFINDINFOA lpFindInfo)
4452 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4453 LVFINDINFOW fiw;
4454 LRESULT res;
4456 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4457 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4458 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4459 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4460 return res;
4463 /***
4464 * DESCRIPTION:
4465 * Retrieves the background image of the listview control.
4467 * PARAMETER(S):
4468 * [I] infoPtr : valid pointer to the listview structure
4469 * [O] LPLVMKBIMAGE : background image attributes
4471 * RETURN:
4472 * SUCCESS : TRUE
4473 * FAILURE : FALSE
4475 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4476 /* { */
4477 /* FIXME (listview, "empty stub!\n"); */
4478 /* return FALSE; */
4479 /* } */
4481 /***
4482 * DESCRIPTION:
4483 * Retrieves column attributes.
4485 * PARAMETER(S):
4486 * [I] infoPtr : valid pointer to the listview structure
4487 * [I] INT : column index
4488 * [IO] LPLVCOLUMNW : column information
4489 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4490 * otherwise it is in fact a LPLVCOLUMNA
4492 * RETURN:
4493 * SUCCESS : TRUE
4494 * FAILURE : FALSE
4496 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4498 HDITEMW hdi;
4499 BOOL bResult = FALSE;
4501 if (lpColumn != NULL)
4504 /* initialize memory */
4505 ZeroMemory(&hdi, sizeof(hdi));
4507 if (lpColumn->mask & LVCF_FMT)
4508 hdi.mask |= HDI_FORMAT;
4510 if (lpColumn->mask & LVCF_WIDTH)
4511 hdi.mask |= HDI_WIDTH;
4513 if (lpColumn->mask & LVCF_TEXT)
4515 hdi.mask |= HDI_TEXT;
4516 hdi.cchTextMax = lpColumn->cchTextMax;
4517 hdi.pszText = lpColumn->pszText;
4520 if (lpColumn->mask & LVCF_IMAGE)
4521 hdi.mask |= HDI_IMAGE;
4523 if (lpColumn->mask & LVCF_ORDER)
4524 hdi.mask |= HDI_ORDER;
4526 if (isW)
4527 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4528 else
4529 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4531 if (bResult)
4533 if (lpColumn->mask & LVCF_FMT)
4535 lpColumn->fmt = 0;
4537 if (hdi.fmt & HDF_LEFT)
4538 lpColumn->fmt |= LVCFMT_LEFT;
4539 else if (hdi.fmt & HDF_RIGHT)
4540 lpColumn->fmt |= LVCFMT_RIGHT;
4541 else if (hdi.fmt & HDF_CENTER)
4542 lpColumn->fmt |= LVCFMT_CENTER;
4544 if (hdi.fmt & HDF_IMAGE)
4545 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4547 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4548 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4551 if (lpColumn->mask & LVCF_WIDTH)
4552 lpColumn->cx = hdi.cxy;
4554 if (lpColumn->mask & LVCF_IMAGE)
4555 lpColumn->iImage = hdi.iImage;
4557 if (lpColumn->mask & LVCF_ORDER)
4558 lpColumn->iOrder = hdi.iOrder;
4560 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4561 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4566 return bResult;
4570 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4572 INT i;
4574 if (!lpiArray)
4575 return FALSE;
4577 /* FIXME: little hack */
4578 for (i = 0; i < iCount; i++)
4579 lpiArray[i] = i;
4581 return TRUE;
4584 /***
4585 * DESCRIPTION:
4586 * Retrieves the column width.
4588 * PARAMETER(S):
4589 * [I] infoPtr : valid pointer to the listview structure
4590 * [I] int : column index
4592 * RETURN:
4593 * SUCCESS : column width
4594 * FAILURE : zero
4596 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4598 INT nColumnWidth = 0;
4599 HDITEMW hdi;
4601 TRACE("nColumn=%d\n", nColumn);
4603 switch(LISTVIEW_GetType(infoPtr))
4605 case LVS_LIST:
4606 nColumnWidth = infoPtr->nItemWidth;
4607 break;
4608 case LVS_REPORT:
4609 hdi.mask = HDI_WIDTH;
4610 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4611 nColumnWidth = hdi.cxy;
4612 break;
4613 default:
4614 /* we don't have a 'column' in [SMALL]ICON mode */
4617 TRACE("nColumnWidth=%d\n", nColumnWidth);
4618 return nColumnWidth;
4621 /***
4622 * DESCRIPTION:
4623 * In list or report display mode, retrieves the number of items that can fit
4624 * vertically in the visible area. In icon or small icon display mode,
4625 * retrieves the total number of visible items.
4627 * PARAMETER(S):
4628 * [I] infoPtr : valid pointer to the listview structure
4630 * RETURN:
4631 * Number of fully visible items.
4633 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4635 UINT uView = LISTVIEW_GetType(infoPtr);
4636 INT nItemCount = 0;
4638 if (uView == LVS_LIST)
4640 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4642 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4643 LISTVIEW_GetCountPerColumn(infoPtr);
4646 else if (uView == LVS_REPORT)
4648 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4650 else
4652 nItemCount = GETITEMCOUNT(infoPtr);
4655 return nItemCount;
4659 /***
4660 * DESCRIPTION:
4661 * Retrieves an image list handle.
4663 * PARAMETER(S):
4664 * [I] infoPtr : valid pointer to the listview structure
4665 * [I] nImageList : image list identifier
4667 * RETURN:
4668 * SUCCESS : image list handle
4669 * FAILURE : NULL
4671 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4673 HIMAGELIST himl = NULL;
4675 switch (nImageList)
4677 case LVSIL_NORMAL:
4678 himl = infoPtr->himlNormal;
4679 break;
4680 case LVSIL_SMALL:
4681 himl = infoPtr->himlSmall;
4682 break;
4683 case LVSIL_STATE:
4684 himl = infoPtr->himlState;
4685 break;
4688 return (LRESULT)himl;
4691 /* LISTVIEW_GetISearchString */
4693 /***
4694 * Helper function for LISTVIEW_GetItemT *only*. Tests if an item is selected.
4695 * It is important that no other functions call this because of callbacks.
4697 static inline BOOL is_item_selected(LISTVIEW_INFO *infoPtr, INT nItem)
4699 RANGE selection = { nItem, nItem };
4701 return DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
4702 LISTVIEW_CompareSelectionRanges, 0, DPAS_SORTED) != -1;
4705 /***
4706 * DESCRIPTION:
4707 * Retrieves item attributes.
4709 * PARAMETER(S):
4710 * [I] hwnd : window handle
4711 * [IO] lpLVItem : item info
4712 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4713 * if FALSE, the lpLVItem is a LPLVITEMA.
4715 * NOTE:
4716 * This is the internal 'GetItem' interface -- it tries to
4717 * be smart, and avoids text copies, if possible, by modifing
4718 * lpLVItem->pszText to point to the text string. Please note
4719 * that this is not always possible (e.g. OWNERDATA), so on
4720 * entry you *must* supply valid values for pszText, and cchTextMax.
4721 * The only difference to the documented interface is that upon
4722 * return, you should use *only* the lpLVItem->pszText, rather than
4723 * the buffer pointer you provided on input. Most code already does
4724 * that, so it's not a problem.
4725 * For the two cases when the text must be copied (that is,
4726 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4728 * RETURN:
4729 * SUCCESS : TRUE
4730 * FAILURE : FALSE
4732 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4734 NMLVDISPINFOW dispInfo;
4735 LISTVIEW_ITEM *lpItem;
4736 ITEMHDR* pItemHdr;
4737 HDPA hdpaSubItems;
4739 /* In the following:
4740 * lpLVItem describes the information requested by the user
4741 * lpItem is what we have
4742 * dispInfo is a structure we use to request the missing
4743 * information from the application
4746 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4748 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4749 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
4750 return FALSE;
4752 /* a quick optimization if all we're asked is the focus state
4753 * these queries are worth optimising since they are common,
4754 * and can be answered in constant time, without the heavy accesses */
4755 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4756 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4758 lpLVItem->state = 0;
4759 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4760 lpLVItem->state |= LVIS_FOCUSED;
4761 return TRUE;
4764 ZeroMemory(&dispInfo, sizeof(dispInfo));
4766 /* if the app stores all the data, handle it separately */
4767 if (infoPtr->dwStyle & LVS_OWNERDATA)
4769 dispInfo.item.state = 0;
4771 /* if we need to callback, do it now */
4772 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4774 /* NOTE: copy only fields which we _know_ are initialized, some apps
4775 * depend on the uninitialized fields being 0 */
4776 dispInfo.item.mask = lpLVItem->mask;
4777 dispInfo.item.iItem = lpLVItem->iItem;
4778 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4779 if (lpLVItem->mask & LVIF_TEXT)
4781 dispInfo.item.pszText = lpLVItem->pszText;
4782 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4784 if (lpLVItem->mask & LVIF_STATE)
4785 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4786 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4787 dispInfo.item.stateMask = lpLVItem->stateMask;
4788 *lpLVItem = dispInfo.item;
4789 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4792 /* we store only a little state, so if we're not asked, we're done */
4793 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4795 /* if focus is handled by us, report it */
4796 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4798 lpLVItem->state &= ~LVIS_FOCUSED;
4799 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4800 lpLVItem->state |= LVIS_FOCUSED;
4803 /* and do the same for selection, if we handle it */
4804 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4806 lpLVItem->state &= ~LVIS_SELECTED;
4807 if (is_item_selected(infoPtr, lpLVItem->iItem))
4808 lpLVItem->state |= LVIS_SELECTED;
4811 return TRUE;
4814 /* find the item and subitem structures before we proceed */
4815 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4816 if (hdpaSubItems == NULL) return FALSE;
4818 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4819 return FALSE;
4821 if (lpLVItem->iSubItem)
4823 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4824 if(!lpSubItem) return FALSE;
4825 pItemHdr = &lpSubItem->hdr;
4827 else
4828 pItemHdr = &lpItem->hdr;
4830 /* Do we need to query the state from the app? */
4831 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4833 dispInfo.item.mask |= LVIF_STATE;
4834 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4837 /* Do we need to enquire about the image? */
4838 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4839 dispInfo.item.mask |= LVIF_IMAGE;
4841 /* Do we need to enquire about the text? */
4842 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4844 dispInfo.item.mask |= LVIF_TEXT;
4845 dispInfo.item.pszText = lpLVItem->pszText;
4846 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4847 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4848 *dispInfo.item.pszText = '\0';
4851 /* If we don't have all the requested info, query the application */
4852 if (dispInfo.item.mask != 0)
4854 dispInfo.item.iItem = lpLVItem->iItem;
4855 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4856 dispInfo.item.lParam = lpItem->lParam;
4857 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4858 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4861 /* Now, handle the iImage field */
4862 if (dispInfo.item.mask & LVIF_IMAGE)
4864 lpLVItem->iImage = dispInfo.item.iImage;
4865 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4866 pItemHdr->iImage = dispInfo.item.iImage;
4868 else if (lpLVItem->mask & LVIF_IMAGE)
4869 lpLVItem->iImage = pItemHdr->iImage;
4871 /* The pszText field */
4872 if (dispInfo.item.mask & LVIF_TEXT)
4874 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4875 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4877 lpLVItem->pszText = dispInfo.item.pszText;
4879 else if (lpLVItem->mask & LVIF_TEXT)
4881 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4882 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4885 /* if this is a subitem, we're done*/
4886 if (lpLVItem->iSubItem) return TRUE;
4888 /* Next is the lParam field */
4889 if (dispInfo.item.mask & LVIF_PARAM)
4891 lpLVItem->lParam = dispInfo.item.lParam;
4892 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4893 lpItem->lParam = dispInfo.item.lParam;
4895 else if (lpLVItem->mask & LVIF_PARAM)
4896 lpLVItem->lParam = lpItem->lParam;
4898 /* ... the state field (this one is different due to uCallbackmask) */
4899 if (lpLVItem->mask & LVIF_STATE)
4901 lpLVItem->state = lpItem->state;
4902 if (dispInfo.item.mask & LVIF_STATE)
4904 lpLVItem->state &= ~dispInfo.item.stateMask;
4905 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4907 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4909 lpLVItem->state &= ~LVIS_FOCUSED;
4910 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4911 lpLVItem->state |= LVIS_FOCUSED;
4913 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4915 lpLVItem->state &= ~LVIS_SELECTED;
4916 if (is_item_selected(infoPtr, lpLVItem->iItem))
4917 lpLVItem->state |= LVIS_SELECTED;
4921 /* and last, but not least, the indent field */
4922 if (lpLVItem->mask & LVIF_INDENT)
4923 lpLVItem->iIndent = lpItem->iIndent;
4925 return TRUE;
4928 /***
4929 * DESCRIPTION:
4930 * Retrieves item attributes.
4932 * PARAMETER(S):
4933 * [I] hwnd : window handle
4934 * [IO] lpLVItem : item info
4935 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4936 * if FALSE, the lpLVItem is a LPLVITEMA.
4938 * NOTE:
4939 * This is the external 'GetItem' interface -- it properly copies
4940 * the text in the provided buffer.
4942 * RETURN:
4943 * SUCCESS : TRUE
4944 * FAILURE : FALSE
4946 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4948 LPWSTR pszText;
4949 BOOL bResult;
4951 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4952 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
4953 return FALSE;
4955 pszText = lpLVItem->pszText;
4956 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4957 if (bResult && lpLVItem->pszText != pszText)
4958 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4959 lpLVItem->pszText = pszText;
4961 return bResult;
4965 /***
4966 * DESCRIPTION:
4967 * Retrieves the position (upper-left) of the listview control item.
4968 * Note that for LVS_ICON style, the upper-left is that of the icon
4969 * and not the bounding box.
4971 * PARAMETER(S):
4972 * [I] infoPtr : valid pointer to the listview structure
4973 * [I] INT : item index
4974 * [O] LPPOINT : coordinate information
4976 * RETURN:
4977 * SUCCESS : TRUE
4978 * FAILURE : FALSE
4980 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4982 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4984 if ((nItem < 0) || (nItem >= GETITEMCOUNT(infoPtr)) || !lpptPosition) return FALSE;
4986 return LISTVIEW_GetItemMeasures(infoPtr, nItem, lpptPosition, NULL, NULL, NULL, NULL);
4989 /***
4990 * Adjust a text rectangle to an integral number of text lines.
4992 static void LISTVIEW_GetIntegralLines(
4993 const LISTVIEW_INFO *infoPtr,
4994 RECT *rcText)
4996 INT i, j, k, l;
4999 * We need to have the bottom to be an intergal number of
5000 * text lines (ntmHeight) below text top that is less than
5001 * or equal to the nItemHeight.
5003 i = infoPtr->nItemHeight - infoPtr->iconSize.cy -
5004 ICON_TOP_PADDING - ICON_BOTTOM_PADDING;
5005 j = i / infoPtr->ntmHeight;
5006 k = j * infoPtr->ntmHeight;
5007 l = rcText->top + k;
5008 rcText->bottom = min(rcText->bottom, l);
5009 rcText->bottom += 1;
5011 TRACE("integral lines, nitemH=%d, ntmH=%d, icon.cy=%ld, i=%d, j=%d, k=%d, rect=(%d,%d)-(%d,%d)\n",
5012 infoPtr->nItemHeight, infoPtr->ntmHeight, infoPtr->iconSize.cy,
5013 i, j, k,
5014 rcText->left, rcText->top, rcText->right, rcText->bottom);
5018 /***
5019 * DESCRIPTION: [INTERNAL]
5020 * Update the bounding rectangle around the text under a large icon.
5021 * This depends on whether it has the focus or not.
5022 * On entry the rectangle's top, left and right should be set.
5023 * On return the bottom will also be set and the width may have been
5024 * modified.
5026 * PARAMETER
5027 * [I] infoPtr : pointer to the listview structure
5028 * [I] nItem : the item for which we are calculating this
5029 * [I/O] rect : the rectangle to be updated
5031 * This appears to be weird, even in the Microsoft implementation.
5033 static BOOL LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *infoPtr, int nItem, RECT *rect)
5035 HDC hdc = GetDC (infoPtr->hwndSelf);
5036 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5037 UINT uFormat = LISTVIEW_DTFLAGS | DT_CALCRECT;
5038 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5039 RECT rcText = *rect;
5040 RECT rcBack = *rect;
5041 BOOL focused, selected;
5042 int dx, dy, old_wid, new_wid;
5043 LVITEMW lvItem;
5045 TRACE("%s, focus item=%d, cur item=%d\n",
5046 (infoPtr->bFocus) ? "Window has focus" : "Window not focused",
5047 infoPtr->nFocusedItem, nItem);
5050 focused = infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED);
5051 selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
5053 uFormat |= (focused) ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
5055 /* We (aim to) display the full text. In Windows 95 it appears to
5056 * calculate the size assuming the specified font and then it draws
5057 * the text in that region with the specified font except scaled to
5058 * 10 point (or the height of the system font or ...). Thus if the
5059 * window has 24 point Helvetica the highlit rectangle will be
5060 * taller than the text and if it is 7 point Helvetica then the text
5061 * will be clipped.
5062 * For now we will simply say that it is the correct size to display
5063 * the text in the specified font.
5065 lvItem.mask = LVIF_TEXT;
5066 lvItem.iItem = nItem;
5067 lvItem.iSubItem = 0;
5068 lvItem.pszText = szDispText;
5069 lvItem.cchTextMax = DISP_TEXT_SIZE;
5070 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5072 InflateRect(&rcText, -2, 0);
5073 DrawTextW (hdc, lvItem.pszText, -1, &rcText, uFormat);
5074 /* Microsoft, in their great wisdom, have decided that the rectangle
5075 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
5076 * not the location. So we have to do the centring ourselves (and take
5077 * responsibility for agreeing off-by-one consistency with them).
5080 old_wid = rcText.right - rcText.left;
5081 new_wid = rcBack.right - rcBack.left;
5082 dx = rcBack.left - rcText.left + (new_wid-old_wid)/2;
5083 dy = rcBack.top - rcText.top;
5084 OffsetRect (&rcText, dx, dy);
5086 if (focused)
5088 rcText.bottom += 2;
5090 else /* not focused, may or may not be selected */
5092 LISTVIEW_GetIntegralLines(infoPtr, &rcText);
5094 *rect = rcText;
5096 TRACE("%s and %s, bounding rect=(%d,%d)-(%d,%d)\n",
5097 (focused) ? "focused(full text)" : "not focused",
5098 (selected) ? "selected" : "not selected",
5099 rect->left, rect->top, rect->right, rect->bottom);
5101 SelectObject (hdc, hOldFont);
5102 ReleaseDC (infoPtr->hwndSelf, hdc);
5104 return TRUE;
5107 /***
5108 * DESCRIPTION:
5109 * Retrieves the bounding rectangle for a listview control item.
5111 * PARAMETER(S):
5112 * [I] infoPtr : valid pointer to the listview structure
5113 * [I] nItem : item index
5114 * [IO] lprc : bounding rectangle coordinates
5115 * lprc->left specifies the portion of the item for which the bounding
5116 * rectangle will be retrieved.
5118 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5119 * including the icon and label.
5121 * * For LVS_ICON
5122 * * Experiment shows that native control returns:
5123 * * width = min (48, length of text line)
5124 * * .left = position.x - (width - iconsize.cx)/2
5125 * * .right = .left + width
5126 * * height = #lines of text * ntmHeight + icon height + 8
5127 * * .top = position.y - 2
5128 * * .bottom = .top + height
5129 * * separation between items .y = itemSpacing.cy - height
5130 * * .x = itemSpacing.cx - width
5131 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5133 * * For LVS_ICON
5134 * * Experiment shows that native control returns:
5135 * * width = iconSize.cx + 16
5136 * * .left = position.x - (width - iconsize.cx)/2
5137 * * .right = .left + width
5138 * * height = iconSize.cy + 4
5139 * * .top = position.y - 2
5140 * * .bottom = .top + height
5141 * * separation between items .y = itemSpacing.cy - height
5142 * * .x = itemSpacing.cx - width
5143 * LVIR_LABEL Returns the bounding rectangle of the item text.
5145 * * For LVS_ICON
5146 * * Experiment shows that native control returns:
5147 * * width = text length
5148 * * .left = position.x - width/2
5149 * * .right = .left + width
5150 * * height = ntmH * linecount + 2
5151 * * .top = position.y + iconSize.cy + 6
5152 * * .bottom = .top + height
5153 * * separation between items .y = itemSpacing.cy - height
5154 * * .x = itemSpacing.cx - width
5155 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5156 * rectangles, but excludes columns in report view.
5158 * RETURN:
5159 * SUCCESS : TRUE
5160 * FAILURE : FALSE
5162 * NOTES
5163 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5164 * upon whether the window has the focus currently and on whether the item
5165 * is the one with the focus. Ensure that the control's record of which
5166 * item has the focus agrees with the items' records.
5168 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5170 RECT label_rect, icon_rect;
5172 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5174 if ((nItem < 0) || (nItem >= GETITEMCOUNT(infoPtr)) || !lprc) return FALSE;
5176 switch(lprc->left)
5178 case LVIR_ICON:
5179 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, lprc, NULL, NULL)) return FALSE;
5180 break;
5182 case LVIR_LABEL:
5183 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, NULL, lprc, NULL)) return FALSE;
5184 break;
5186 case LVIR_BOUNDS:
5187 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, NULL, NULL)) return FALSE;
5188 break;
5190 case LVIR_SELECTBOUNDS:
5191 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, &icon_rect, &label_rect, NULL)) return FALSE;
5192 UnionRect (lprc, &icon_rect, &label_rect);
5193 break;
5195 default:
5196 WARN("Unknown value: %d\n", lprc->left);
5197 return FALSE;
5200 TRACE(" rect=%s\n", debugrect(lprc));
5202 return TRUE;
5206 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem, INT flags, LPRECT lprc)
5208 UINT uView = LISTVIEW_GetType(infoPtr);
5209 INT count;
5211 TRACE("(nItem=%d, nSubItem=%d lprc=%p)\n", nItem, nSubItem,
5212 lprc);
5214 if (!(uView & LVS_REPORT))
5215 return FALSE;
5217 if (flags & LVIR_ICON)
5219 FIXME("Unimplemented LVIR_ICON\n");
5220 return FALSE;
5222 else
5224 int top = min(Header_GetItemCount(infoPtr->hwndHeader), nSubItem - 1);
5226 LISTVIEW_GetItemRect(infoPtr,nItem,lprc);
5227 for (count = 0; count < top; count++)
5228 lprc->left += LISTVIEW_GetColumnWidth(infoPtr,count);
5230 lprc->right = LISTVIEW_GetColumnWidth(infoPtr,(nSubItem-1)) +
5231 lprc->left;
5233 return TRUE;
5237 /***
5238 * DESCRIPTION:
5239 * Retrieves the width of a label.
5241 * PARAMETER(S):
5242 * [I] infoPtr : valid pointer to the listview structure
5244 * RETURN:
5245 * SUCCESS : string width (in pixels)
5246 * FAILURE : zero
5248 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5250 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5251 LVITEMW lvItem;
5253 TRACE("(nItem=%d)\n", nItem);
5255 lvItem.mask = LVIF_TEXT;
5256 lvItem.iItem = nItem;
5257 lvItem.iSubItem = 0;
5258 lvItem.pszText = szDispText;
5259 lvItem.cchTextMax = DISP_TEXT_SIZE;
5260 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5262 /* FIXME: is this right? What if the label is very long? */
5263 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5266 /***
5267 * DESCRIPTION:
5268 * Retrieves the spacing between listview control items.
5270 * PARAMETER(S):
5271 * [I] infoPtr : valid pointer to the listview structure
5272 * [I] BOOL : flag for small or large icon
5274 * RETURN:
5275 * Horizontal + vertical spacing
5277 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5279 LONG lResult;
5281 if (!bSmall)
5283 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5285 else
5287 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
5288 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5289 else
5290 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5292 return lResult;
5295 /***
5296 * DESCRIPTION:
5297 * Retrieves the state of a listview control item.
5299 * PARAMETER(S):
5300 * [I] infoPtr : valid pointer to the listview structure
5301 * [I] nItem : item index
5302 * [I] uMask : state mask
5304 * RETURN:
5305 * State specified by the mask.
5307 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5309 LVITEMW lvItem;
5311 if ((nItem < 0) || (nItem >= GETITEMCOUNT(infoPtr))) return 0;
5313 lvItem.iItem = nItem;
5314 lvItem.iSubItem = 0;
5315 lvItem.mask = LVIF_STATE;
5316 lvItem.stateMask = uMask;
5317 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5319 return lvItem.state & uMask;
5322 /***
5323 * DESCRIPTION:
5324 * Retrieves the text of a listview control item or subitem.
5326 * PARAMETER(S):
5327 * [I] hwnd : window handle
5328 * [I] nItem : item index
5329 * [IO] lpLVItem : item information
5330 * [I] isW : TRUE if lpLVItem is Unicode
5332 * RETURN:
5333 * SUCCESS : string length
5334 * FAILURE : 0
5336 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5338 if (!lpLVItem || (nItem < 0) || (nItem >= GETITEMCOUNT(infoPtr))) return 0;
5340 lpLVItem->mask = LVIF_TEXT;
5341 lpLVItem->iItem = nItem;
5342 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5344 return textlenT(lpLVItem->pszText, isW);
5347 /***
5348 * DESCRIPTION:
5349 * Searches for an item based on properties + relationships.
5351 * PARAMETER(S):
5352 * [I] infoPtr : valid pointer to the listview structure
5353 * [I] nItem : item index
5354 * [I] uFlags : relationship flag
5356 * FIXME:
5357 * This function is ver, very inefficient! Needs work.
5359 * RETURN:
5360 * SUCCESS : item index
5361 * FAILURE : -1
5363 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5365 UINT uView = LISTVIEW_GetType(infoPtr);
5366 UINT uMask = 0;
5367 LVFINDINFOW lvFindInfo;
5368 INT nCountPerColumn;
5369 INT i;
5371 if ((nItem < -1) || (nItem >= GETITEMCOUNT(infoPtr))) return -1;
5373 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5375 if (uFlags & LVNI_CUT)
5376 uMask |= LVIS_CUT;
5378 if (uFlags & LVNI_DROPHILITED)
5379 uMask |= LVIS_DROPHILITED;
5381 if (uFlags & LVNI_FOCUSED)
5382 uMask |= LVIS_FOCUSED;
5384 if (uFlags & LVNI_SELECTED)
5385 uMask |= LVIS_SELECTED;
5387 /* if we're asked for the focused item, that's only one,
5388 * so it's worth optimizing */
5389 if (uFlags & LVNI_FOCUSED)
5391 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5392 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5395 if (uFlags & LVNI_ABOVE)
5397 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5399 while (nItem >= 0)
5401 nItem--;
5402 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5403 return nItem;
5406 else
5408 lvFindInfo.flags = LVFI_NEARESTXY;
5409 lvFindInfo.vkDirection = VK_UP;
5410 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5411 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5413 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5414 return nItem;
5418 else if (uFlags & LVNI_BELOW)
5420 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5422 while (nItem < GETITEMCOUNT(infoPtr))
5424 nItem++;
5425 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5426 return nItem;
5429 else
5431 lvFindInfo.flags = LVFI_NEARESTXY;
5432 lvFindInfo.vkDirection = VK_DOWN;
5433 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5434 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5436 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5437 return nItem;
5441 else if (uFlags & LVNI_TOLEFT)
5443 if (uView == LVS_LIST)
5445 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5446 while (nItem - nCountPerColumn >= 0)
5448 nItem -= nCountPerColumn;
5449 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5450 return nItem;
5453 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5455 lvFindInfo.flags = LVFI_NEARESTXY;
5456 lvFindInfo.vkDirection = VK_LEFT;
5457 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5458 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5460 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5461 return nItem;
5465 else if (uFlags & LVNI_TORIGHT)
5467 if (uView == LVS_LIST)
5469 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5470 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5472 nItem += nCountPerColumn;
5473 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5474 return nItem;
5477 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5479 lvFindInfo.flags = LVFI_NEARESTXY;
5480 lvFindInfo.vkDirection = VK_RIGHT;
5481 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5482 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5484 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5485 return nItem;
5489 else
5491 nItem++;
5493 /* search by index */
5494 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5496 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5497 return i;
5501 return -1;
5504 /* LISTVIEW_GetNumberOfWorkAreas */
5506 /***
5507 * DESCRIPTION:
5508 * Retrieves the origin coordinates when in icon or small icon display mode.
5510 * PARAMETER(S):
5511 * [I] infoPtr : valid pointer to the listview structure
5512 * [O] lpptOrigin : coordinate information
5514 * RETURN:
5515 * SUCCESS : TRUE
5516 * FAILURE : FALSE
5518 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5520 DWORD lStyle = infoPtr->dwStyle;
5521 UINT uView = lStyle & LVS_TYPEMASK;
5522 INT nHorzPos = 0, nVertPos = 0;
5523 SCROLLINFO scrollInfo;
5525 if (!lpptOrigin) return FALSE;
5527 scrollInfo.cbSize = sizeof(SCROLLINFO);
5528 scrollInfo.fMask = SIF_POS;
5530 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5531 nHorzPos = scrollInfo.nPos;
5532 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5533 nVertPos = scrollInfo.nPos;
5535 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5537 lpptOrigin->x = infoPtr->rcList.left;
5538 lpptOrigin->y = infoPtr->rcList.top;
5539 if (uView == LVS_LIST)
5541 nHorzPos *= LISTVIEW_GetCountPerColumn(infoPtr);
5542 nVertPos = 0;
5544 else if (uView == LVS_REPORT)
5546 nVertPos *= infoPtr->nItemHeight;
5549 lpptOrigin->x -= nHorzPos;
5550 lpptOrigin->y -= nVertPos;
5552 TRACE("(pt=(%ld,%ld))\n", lpptOrigin->x, lpptOrigin->y);
5554 return TRUE;
5557 /***
5558 * DESCRIPTION:
5559 * Retrieves the width of a string.
5561 * PARAMETER(S):
5562 * [I] hwnd : window handle
5563 * [I] lpszText : text string to process
5564 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5566 * RETURN:
5567 * SUCCESS : string width (in pixels)
5568 * FAILURE : zero
5570 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5572 SIZE stringSize;
5574 stringSize.cx = 0;
5575 if (is_textT(lpszText, isW))
5577 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5578 HDC hdc = GetDC(infoPtr->hwndSelf);
5579 HFONT hOldFont = SelectObject(hdc, hFont);
5581 if (isW)
5582 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5583 else
5584 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5585 SelectObject(hdc, hOldFont);
5586 ReleaseDC(infoPtr->hwndSelf, hdc);
5588 return stringSize.cx;
5592 /***
5593 * DESCRIPTION:
5594 * Determines item if a hit or closest if not
5596 * PARAMETER(S):
5597 * [I] infoPtr : valid pointer to the listview structure
5598 * [IO] lpht : hit test information
5599 * [I] subitem : fill out iSubItem.
5600 * [I] bNearItem : return the nearest item
5602 * RETURN:
5603 * SUCCESS : item index of hit
5604 * FAILURE : -1
5606 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL bNearItem)
5608 LONG lStyle = infoPtr->dwStyle;
5609 UINT uView = lStyle & LVS_TYPEMASK;
5610 INT i,j,topindex,bottomindex,nearestItem;
5611 RECT rcItem,rcSubItem;
5612 DWORD xterm, yterm, dist, mindist;
5614 TRACE("(x=%ld, y=%ld)\n", lpht->pt.x, lpht->pt.y);
5616 nearestItem = -1;
5617 mindist = -1;
5619 /* FIXME: get the visible range */
5620 topindex = LISTVIEW_GetTopIndex(infoPtr);
5621 if (uView == LVS_REPORT)
5623 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
5624 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
5626 else
5628 bottomindex = GETITEMCOUNT(infoPtr);
5631 for (i = topindex; i < bottomindex; i++)
5633 rcItem.left = LVIR_BOUNDS;
5634 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5636 if (PtInRect(&rcItem, lpht->pt))
5638 rcSubItem = rcItem;
5639 rcItem.left = LVIR_ICON;
5640 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5642 if (PtInRect(&rcItem, lpht->pt))
5644 lpht->flags = LVHT_ONITEMICON;
5645 lpht->iItem = i;
5646 goto set_subitem;
5650 rcItem.left = LVIR_LABEL;
5651 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5653 if (PtInRect(&rcItem, lpht->pt))
5655 lpht->flags = LVHT_ONITEMLABEL;
5656 lpht->iItem = i;
5657 goto set_subitem;
5661 lpht->flags = LVHT_ONITEMSTATEICON;
5662 lpht->iItem = i;
5663 set_subitem:
5664 if (subitem)
5666 INT nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5667 lpht->iSubItem = 0;
5668 rcSubItem.right = rcSubItem.left;
5669 for (j = 0; j < nColumnCount; j++)
5671 rcSubItem.left = rcSubItem.right;
5672 rcSubItem.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5673 if (PtInRect(&rcSubItem, lpht->pt))
5675 lpht->iSubItem = j;
5676 break;
5680 TRACE("hit on item %d\n", i);
5681 return i;
5683 else if (bNearItem)
5686 * Now compute distance from point to center of boundary
5687 * box. Since we are only interested in the relative
5688 * distance, we can skip the nasty square root operation
5690 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpht->pt.x;
5691 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpht->pt.y;
5692 dist = xterm * xterm + yterm * yterm;
5693 if (mindist < 0 || dist < mindist)
5695 mindist = dist;
5696 nearestItem = i;
5702 lpht->flags = LVHT_NOWHERE;
5704 return bNearItem ? nearestItem : -1;
5708 /***
5709 * DESCRIPTION:
5710 * Determines which listview item is located at the specified position.
5712 * PARAMETER(S):
5713 * [I] infoPtr : valid pointer to the listview structure
5714 * [IO] lpht : hit test information
5715 * [I] subitem : fill out iSubItem.
5717 * RETURN:
5718 * SUCCESS : item index
5719 * FAILURE : -1
5721 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem)
5723 TRACE("(x=%ld, y=%ld)\n", lpht->pt.x, lpht->pt.y);
5725 lpht->flags = 0;
5727 if (infoPtr->rcList.left > lpht->pt.x)
5728 lpht->flags |= LVHT_TOLEFT;
5729 else if (infoPtr->rcList.right < lpht->pt.x)
5730 lpht->flags |= LVHT_TORIGHT;
5732 if (infoPtr->rcList.top > lpht->pt.y)
5733 lpht->flags |= LVHT_ABOVE;
5734 else if (infoPtr->rcList.bottom < lpht->pt.y)
5735 lpht->flags |= LVHT_BELOW;
5737 if (lpht->flags) return -1;
5739 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
5740 * an app might pass only a structure with space up to iItem!
5741 * (MS Office 97 does that for instance in the file open dialog)
5743 return LISTVIEW_SuperHitTestItem(infoPtr, lpht, subitem, FALSE);
5747 /***
5748 * DESCRIPTION:
5749 * Inserts a new column.
5751 * PARAMETER(S):
5752 * [I] infoPtr : valid pointer to the listview structure
5753 * [I] INT : column index
5754 * [I] LPLVCOLUMNW : column information
5756 * RETURN:
5757 * SUCCESS : new column index
5758 * FAILURE : -1
5760 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5761 LPLVCOLUMNW lpColumn, BOOL isW)
5763 RECT rcOld, rcCol;
5764 INT nNewColumn;
5765 HDITEMW hdi;
5767 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5769 if (!lpColumn) return -1;
5771 hdi.mask = hdi.fmt = 0;
5772 if (lpColumn->mask & LVCF_FMT)
5774 /* format member is valid */
5775 hdi.mask |= HDI_FORMAT;
5777 /* set text alignment (leftmost column must be left-aligned) */
5778 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5779 hdi.fmt |= HDF_LEFT;
5780 else if (lpColumn->fmt & LVCFMT_RIGHT)
5781 hdi.fmt |= HDF_RIGHT;
5782 else if (lpColumn->fmt & LVCFMT_CENTER)
5783 hdi.fmt |= HDF_CENTER;
5785 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5786 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5788 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5790 hdi.fmt |= HDF_IMAGE;
5791 hdi.iImage = I_IMAGECALLBACK;
5794 if (lpColumn->fmt & LVCFMT_IMAGE)
5795 ; /* FIXME: enable images for *(sub)items* this column */
5798 if (lpColumn->mask & LVCF_WIDTH)
5800 hdi.mask |= HDI_WIDTH;
5801 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5803 /* make it fill the remainder of the controls width */
5804 HDITEMW hdit;
5805 RECT rcHeader;
5806 INT item_index;
5808 /* get the width of every item except the current one */
5809 hdit.mask = HDI_WIDTH;
5810 hdi.cxy = 0;
5812 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5813 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5814 hdi.cxy += hdit.cxy;
5816 /* retrieve the layout of the header */
5817 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5818 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5820 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5822 else
5823 hdi.cxy = lpColumn->cx;
5826 if (lpColumn->mask & LVCF_TEXT)
5828 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5829 hdi.fmt |= HDF_STRING;
5830 hdi.pszText = lpColumn->pszText;
5831 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5834 if (lpColumn->mask & LVCF_IMAGE)
5836 hdi.mask |= HDI_IMAGE;
5837 hdi.iImage = lpColumn->iImage;
5840 if (lpColumn->mask & LVCF_ORDER)
5842 hdi.mask |= HDI_ORDER;
5843 hdi.iOrder = lpColumn->iOrder;
5846 /* insert item in header control */
5847 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5848 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5849 (WPARAM)nColumn, (LPARAM)&hdi);
5850 if (nNewColumn == -1) return -1;
5851 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5853 /* now we have to actually adjust the data */
5854 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->hdpaItems->nItemCount > 0)
5856 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5857 HDPA hdpaSubItems;
5858 INT nItem, i;
5860 /* preallocate memory, so we can fail gracefully */
5861 if (nNewColumn == 0)
5863 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->hdpaItems->nItemCount);
5864 if (!lpNewItems) return -1;
5865 for (i = 0; i < infoPtr->hdpaItems->nItemCount; i++)
5866 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5867 if (i != infoPtr->hdpaItems->nItemCount)
5869 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5870 COMCTL32_Free(lpNewItems);
5871 return -1;
5875 for (nItem = 0; nItem < infoPtr->hdpaItems->nItemCount; nItem++)
5877 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5878 if (!hdpaSubItems) continue;
5879 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5881 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5882 if (!lpSubItem) break;
5883 if (lpSubItem->iSubItem >= nNewColumn)
5884 lpSubItem->iSubItem++;
5887 /* if we found our subitem, zapp it */
5888 if (nNewColumn == 0)
5890 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5891 lpSubItem = lpNewItems[nItem];
5892 lpSubItem->hdr = lpMainItem->hdr;
5893 lpSubItem->iSubItem = 1;
5894 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5895 lpMainItem->iSubItem = 0;
5896 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5900 COMCTL32_Free(lpNewItems);
5903 /* we don't have to worry abiut display issues in non-report mode */
5904 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5906 /* Need to reset the item width when inserting a new column */
5907 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
5909 LISTVIEW_UpdateScroll(infoPtr);
5911 /* scroll to cover the deleted column, and invalidate for redraw */
5912 rcOld = infoPtr->rcList;
5913 rcOld.left = rcCol.left;
5914 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5915 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5917 return nNewColumn;
5920 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5921 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5922 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5923 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5924 their own sort proc. when sending LVM_SORTITEMS.
5926 /* Platform SDK:
5927 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5929 LVS_SORTXXX must be specified,
5930 LVS_OWNERDRAW is not set,
5931 <item>.pszText is not LPSTR_TEXTCALLBACK.
5933 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5934 are sorted based on item text..."
5936 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5938 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5939 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5940 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5942 /* if we're sorting descending, negate the return value */
5943 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5946 /***
5947 * nESCRIPTION:
5948 * Inserts a new item in the listview control.
5950 * PARAMETER(S):
5951 * [I] infoPtr : valid pointer to the listview structure
5952 * [I] lpLVItem : item information
5953 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5955 * RETURN:
5956 * SUCCESS : new item index
5957 * FAILURE : -1
5959 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5961 LONG lStyle = infoPtr->dwStyle;
5962 UINT uView = lStyle & LVS_TYPEMASK;
5963 INT nItem = -1;
5964 HDPA hdpaSubItems;
5965 NMLISTVIEW nmlv;
5966 LISTVIEW_ITEM *lpItem;
5967 BOOL is_sorted;
5969 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5971 if (lStyle & LVS_OWNERDATA)
5973 nItem = infoPtr->hdpaItems->nItemCount;
5974 infoPtr->hdpaItems->nItemCount++;
5975 return nItem;
5978 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5979 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5981 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5983 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5984 return -1;
5986 /* insert item in listview control data structure */
5987 if ( (hdpaSubItems = DPA_Create(8)) )
5988 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5989 if (nItem == -1) goto fail;
5991 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5992 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5994 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5995 is_sorted ? GETITEMCOUNT( infoPtr ) + 1 : lpLVItem->iItem,
5996 hdpaSubItems );
5997 if (nItem == -1) goto fail;
5999 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
6001 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6002 goto fail;
6005 /* if we're sorted, sort the list, and update the index */
6006 if (is_sorted)
6008 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6009 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6010 if (nItem == -1)
6012 ERR("We can't find the item we just inserted, possible memory corruption.");
6013 /* we can't remove it from the list if we can't find it, so just fail */
6014 /* we don't deallocate memory here, as it will probably cause more problems */
6015 return -1;
6019 /* Add the subitem list to the items array. Do this last in case we go to
6020 * fail during the above.
6022 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6024 lpItem->valid = TRUE;
6026 /* send LVN_INSERTITEM notification */
6027 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6028 nmlv.iItem = nItem;
6029 nmlv.lParam = lpItem->lParam;
6030 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6032 /* align items (set position of each item) */
6033 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6035 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
6036 else LISTVIEW_AlignTop(infoPtr);
6039 LISTVIEW_UpdateScroll(infoPtr);
6041 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6043 TRACE(" <- %d\n", nItem);
6044 return nItem;
6046 fail:
6047 DPA_DeletePtr(hdpaSubItems, 0);
6048 DPA_Destroy (hdpaSubItems);
6049 COMCTL32_Free (lpItem);
6050 return -1;
6053 /***
6054 * DESCRIPTION:
6055 * Redraws a range of items.
6057 * PARAMETER(S):
6058 * [I] infoPtr : valid pointer to the listview structure
6059 * [I] INT : first item
6060 * [I] INT : last item
6062 * RETURN:
6063 * SUCCESS : TRUE
6064 * FAILURE : FALSE
6066 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6068 INT i;
6070 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6071 max(nFirst, nLast) >= GETITEMCOUNT(infoPtr))
6072 return FALSE;
6074 for (i = nFirst; i <= nLast; i++)
6075 LISTVIEW_InvalidateItem(infoPtr, i);
6077 return TRUE;
6080 /***
6081 * DESCRIPTION:
6082 * Scroll the content of a listview.
6084 * PARAMETER(S):
6085 * [I] infoPtr : valid pointer to the listview structure
6086 * [I] INT : horizontal scroll amount in pixels
6087 * [I] INT : vertical scroll amount in pixels
6089 * RETURN:
6090 * SUCCESS : TRUE
6091 * FAILURE : FALSE
6093 * COMMENTS:
6094 * If the control is in report mode (LVS_REPORT) the control can
6095 * be scrolled only in line increments. "dy" will be rounded to the
6096 * nearest number of pixels that are a whole line. Ex: if line height
6097 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6098 * is passed the the scroll will be 0. (per MSDN 7/2002)
6100 * For: (per experimentaion with native control and CSpy ListView)
6101 * LVS_ICON dy=1 = 1 pixel (vertical only)
6102 * dx ignored
6103 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6104 * dx ignored
6105 * LVS_LIST dx=1 = 1 column (horizontal only)
6106 * but will only scroll 1 column per message
6107 * no matter what the value.
6108 * dy must be 0 or FALSE returned.
6109 * LVS_REPORT dx=1 = 1 pixel
6110 * dy= see above
6113 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6115 switch(LISTVIEW_GetType(infoPtr)) {
6116 case LVS_REPORT:
6117 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6118 dy /= infoPtr->nItemHeight;
6119 break;
6120 case LVS_LIST:
6121 if (dy != 0) return FALSE;
6122 break;
6123 default: /* icon */
6124 dx = 0;
6125 break;
6128 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6129 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6131 return TRUE;
6134 /***
6135 * DESCRIPTION:
6136 * Sets the background color.
6138 * PARAMETER(S):
6139 * [I] infoPtr : valid pointer to the listview structure
6140 * [I] COLORREF : background color
6142 * RETURN:
6143 * SUCCESS : TRUE
6144 * FAILURE : FALSE
6146 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6148 TRACE("(clrBk=%lx)\n", clrBk);
6150 if(infoPtr->clrBk != clrBk) {
6151 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6152 infoPtr->clrBk = clrBk;
6153 if (clrBk == CLR_NONE)
6154 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6155 else
6156 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6157 LISTVIEW_InvalidateList(infoPtr);
6160 return TRUE;
6163 /* LISTVIEW_SetBkImage */
6165 /***
6166 * DESCRIPTION:
6167 * Sets the attributes of a header item.
6169 * PARAMETER(S):
6170 * [I] infoPtr : valid pointer to the listview structure
6171 * [I] INT : column index
6172 * [I] LPLVCOLUMNW : column attributes
6173 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6174 * otherwise it is in fact a LPLVCOLUMNA
6176 * RETURN:
6177 * SUCCESS : TRUE
6178 * FAILURE : FALSE
6180 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6181 LPLVCOLUMNW lpColumn, BOOL isW)
6183 BOOL bResult = FALSE;
6184 HDITEMW hdi, hdiget;
6186 if ((lpColumn != NULL) && (nColumn >= 0) &&
6187 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6189 /* initialize memory */
6190 ZeroMemory(&hdi, sizeof(hdi));
6192 if (lpColumn->mask & LVCF_FMT)
6194 /* format member is valid */
6195 hdi.mask |= HDI_FORMAT;
6197 /* get current format first */
6198 hdiget.mask = HDI_FORMAT;
6199 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6200 /* preserve HDF_STRING if present */
6201 hdi.fmt = hdiget.fmt & HDF_STRING;
6203 /* set text alignment (leftmost column must be left-aligned) */
6204 if (nColumn == 0)
6206 hdi.fmt |= HDF_LEFT;
6208 else
6210 if (lpColumn->fmt & LVCFMT_LEFT)
6211 hdi.fmt |= HDF_LEFT;
6212 else if (lpColumn->fmt & LVCFMT_RIGHT)
6213 hdi.fmt |= HDF_RIGHT;
6214 else if (lpColumn->fmt & LVCFMT_CENTER)
6215 hdi.fmt |= HDF_CENTER;
6218 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6219 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6221 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6222 hdi.fmt |= HDF_IMAGE;
6224 if (lpColumn->fmt & LVCFMT_IMAGE)
6226 hdi.fmt |= HDF_IMAGE;
6227 hdi.iImage = I_IMAGECALLBACK;
6231 if (lpColumn->mask & LVCF_WIDTH)
6233 hdi.mask |= HDI_WIDTH;
6234 hdi.cxy = lpColumn->cx;
6237 if (lpColumn->mask & LVCF_TEXT)
6239 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6240 hdi.pszText = lpColumn->pszText;
6241 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6242 hdi.fmt |= HDF_STRING;
6245 if (lpColumn->mask & LVCF_IMAGE)
6247 hdi.mask |= HDI_IMAGE;
6248 hdi.iImage = lpColumn->iImage;
6251 if (lpColumn->mask & LVCF_ORDER)
6253 hdi.mask |= HDI_ORDER;
6254 hdi.iOrder = lpColumn->iOrder;
6257 /* set header item attributes */
6258 if (isW)
6259 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6260 else
6261 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6264 return bResult;
6267 /***
6268 * DESCRIPTION:
6269 * Sets the column order array
6271 * PARAMETERS:
6272 * [I] infoPtr : valid pointer to the listview structure
6273 * [I] INT : number of elements in column order array
6274 * [I] INT : pointer to column order array
6276 * RETURN:
6277 * SUCCESS : TRUE
6278 * FAILURE : FALSE
6280 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6282 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6284 if (!lpiArray)
6285 return FALSE;
6287 return TRUE;
6291 /***
6292 * DESCRIPTION:
6293 * Sets the width of a column
6295 * PARAMETERS:
6296 * [I] infoPtr : valid pointer to the listview structure
6297 * [I] INT : column index
6298 * [I] INT : column width
6300 * RETURN:
6301 * SUCCESS : TRUE
6302 * FAILURE : FALSE
6304 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6306 HDITEMW hdi;
6307 LRESULT lret;
6308 LONG lStyle = infoPtr->dwStyle;
6309 UINT uView = lStyle & LVS_TYPEMASK;
6310 HDC hdc;
6311 HFONT header_font;
6312 HFONT old_font;
6313 SIZE size;
6314 WCHAR text_buffer[DISP_TEXT_SIZE];
6315 INT header_item_count;
6316 INT item_index;
6317 INT nLabelWidth;
6318 RECT rcHeader;
6319 LVITEMW lvItem;
6320 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6322 if (!infoPtr->hwndHeader) /* make sure we have a header */
6323 return (FALSE);
6325 /* set column width only if in report or list mode */
6326 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6327 return (FALSE);
6329 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6331 /* take care of invalid cx values */
6332 if((uView == LVS_REPORT) && (cx < -2))
6333 cx = LVSCW_AUTOSIZE;
6334 else if (uView == LVS_LIST && (cx < 1))
6335 return FALSE;
6337 /* resize all columns if in LVS_LIST mode */
6338 if(uView == LVS_LIST) {
6339 infoPtr->nItemWidth = cx;
6340 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6341 return TRUE;
6344 /* autosize based on listview items width */
6345 if(cx == LVSCW_AUTOSIZE)
6347 /* set the width of the column to the width of the widest item */
6348 if (iCol == 0 || uView == LVS_LIST)
6350 cx = 0;
6351 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6353 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6354 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6356 if (infoPtr->himlSmall)
6357 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6359 else
6361 lvItem.iSubItem = iCol;
6362 lvItem.mask = LVIF_TEXT;
6363 lvItem.pszText = szDispText;
6364 lvItem.cchTextMax = DISP_TEXT_SIZE;
6365 cx = 0;
6366 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6368 lvItem.iItem = item_index;
6369 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6370 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6371 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6374 cx += TRAILING_PADDING;
6375 } /* autosize based on listview header width */
6376 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6378 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6380 /* if iCol is the last column make it fill the remainder of the controls width */
6381 if(iCol == (header_item_count - 1)) {
6382 /* get the width of every item except the current one */
6383 hdi.mask = HDI_WIDTH;
6384 cx = 0;
6386 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6387 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6388 cx+=hdi.cxy;
6391 /* retrieve the layout of the header */
6392 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6394 cx = (rcHeader.right - rcHeader.left) - cx;
6396 else
6398 /* Despite what the MS docs say, if this is not the last
6399 column, then MS resizes the column to the width of the
6400 largest text string in the column, including headers
6401 and items. This is different from LVSCW_AUTOSIZE in that
6402 LVSCW_AUTOSIZE ignores the header string length.
6405 /* retrieve header font */
6406 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6408 /* retrieve header text */
6409 hdi.mask = HDI_TEXT;
6410 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6411 hdi.pszText = text_buffer;
6413 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6415 /* determine the width of the text in the header */
6416 hdc = GetDC(infoPtr->hwndSelf);
6417 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6419 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6421 SelectObject(hdc, old_font); /* restore the old font */
6422 ReleaseDC(infoPtr->hwndSelf, hdc);
6424 lvItem.iSubItem = iCol;
6425 lvItem.mask = LVIF_TEXT;
6426 lvItem.pszText = szDispText;
6427 lvItem.cchTextMax = DISP_TEXT_SIZE;
6428 cx = size.cx;
6429 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6431 lvItem.iItem = item_index;
6432 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6433 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6434 nLabelWidth += TRAILING_PADDING;
6435 /* While it is possible for subitems to have icons, even MS messes
6436 up the positioning, so I suspect no applications actually use
6437 them. */
6438 if (item_index == 0 && infoPtr->himlSmall)
6439 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6440 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6445 /* call header to update the column change */
6446 hdi.mask = HDI_WIDTH;
6448 hdi.cxy = cx;
6449 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6451 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6453 return lret;
6456 /***
6457 * DESCRIPTION:
6458 * Sets the extended listview style.
6460 * PARAMETERS:
6461 * [I] infoPtr : valid pointer to the listview structure
6462 * [I] DWORD : mask
6463 * [I] DWORD : style
6465 * RETURN:
6466 * SUCCESS : previous style
6467 * FAILURE : 0
6469 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6471 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6473 /* set new style */
6474 if (dwMask)
6475 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6476 else
6477 infoPtr->dwLvExStyle = dwStyle;
6479 return dwOldStyle;
6482 /***
6483 * DESCRIPTION:
6484 * Sets the new hot cursor used during hot tracking and hover selection.
6486 * PARAMETER(S):
6487 * [I] infoPtr : valid pointer to the listview structure
6488 * [I} hCurosr : the new hot cursor handle
6490 * RETURN:
6491 * Returns the previous hot cursor
6493 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6495 HCURSOR oldCursor = infoPtr->hHotCursor;
6496 infoPtr->hHotCursor = hCursor;
6497 return oldCursor;
6501 /***
6502 * DESCRIPTION:
6503 * Sets the hot item index.
6505 * PARAMETERS:
6506 * [I] infoPtr : valid pointer to the listview structure
6507 * [I] INT : index
6509 * RETURN:
6510 * SUCCESS : previous hot item index
6511 * FAILURE : -1 (no hot item)
6513 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6515 INT iOldIndex = infoPtr->nHotItem;
6516 infoPtr->nHotItem = iIndex;
6517 return iOldIndex;
6521 /***
6522 * DESCRIPTION:
6523 * Sets the amount of time the cursor must hover over an item before it is selected.
6525 * PARAMETER(S):
6526 * [I] infoPtr : valid pointer to the listview structure
6527 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6529 * RETURN:
6530 * Returns the previous hover time
6532 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6534 DWORD oldHoverTime = infoPtr->dwHoverTime;
6535 infoPtr->dwHoverTime = dwHoverTime;
6536 return oldHoverTime;
6539 /***
6540 * DESCRIPTION:
6541 * Sets spacing for icons of LVS_ICON style.
6543 * PARAMETER(S):
6544 * [I] infoPtr : valid pointer to the listview structure
6545 * [I] DWORD : MAKELONG(cx, cy)
6547 * RETURN:
6548 * MAKELONG(oldcx, oldcy)
6550 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6552 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6553 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6554 LONG lStyle = infoPtr->dwStyle;
6555 UINT uView = lStyle & LVS_TYPEMASK;
6557 TRACE("requested=(%d,%d)\n", cx, cy);
6559 /* this is supported only for LVS_ICON style */
6560 if (uView != LVS_ICON) return oldspacing;
6562 /* set to defaults, if instructed to */
6563 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6564 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6566 /* if 0 then compute width
6567 * FIXME: Should scan each item and determine max width of
6568 * icon or label, then make that the width */
6569 if (cx == 0)
6570 cx = infoPtr->iconSpacing.cx;
6572 /* if 0 then compute height */
6573 if (cy == 0)
6574 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6575 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6578 infoPtr->iconSpacing.cx = cx;
6579 infoPtr->iconSpacing.cy = cy;
6581 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6582 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6583 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6584 infoPtr->ntmHeight);
6586 /* these depend on the iconSpacing */
6587 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
6588 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6590 return oldspacing;
6593 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6595 INT cx, cy;
6597 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6599 size->cx = cx;
6600 size->cy = cy;
6602 else
6603 size->cx = size->cy = 0;
6606 /***
6607 * DESCRIPTION:
6608 * Sets image lists.
6610 * PARAMETER(S):
6611 * [I] infoPtr : valid pointer to the listview structure
6612 * [I] INT : image list type
6613 * [I] HIMAGELIST : image list handle
6615 * RETURN:
6616 * SUCCESS : old image list
6617 * FAILURE : NULL
6619 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6621 UINT uView = LISTVIEW_GetType(infoPtr);
6622 INT oldHeight = infoPtr->nItemHeight;
6623 HIMAGELIST himlOld = 0;
6625 switch (nType)
6627 case LVSIL_NORMAL:
6628 himlOld = infoPtr->himlNormal;
6629 infoPtr->himlNormal = himl;
6630 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6631 LISTVIEW_SetIconSpacing(infoPtr, 0);
6632 break;
6634 case LVSIL_SMALL:
6635 himlOld = infoPtr->himlSmall;
6636 infoPtr->himlSmall = himl;
6637 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6638 break;
6640 case LVSIL_STATE:
6641 himlOld = infoPtr->himlState;
6642 infoPtr->himlState = himl;
6643 update_icon_size(himl, &infoPtr->iconStateSize);
6644 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6645 break;
6647 default:
6648 ERR("Unknown icon type=%d\n", nType);
6649 return NULL;
6652 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6653 if (infoPtr->nItemHeight != oldHeight)
6654 LISTVIEW_UpdateScroll(infoPtr);
6656 return himlOld;
6659 /***
6660 * DESCRIPTION:
6661 * Preallocates memory (does *not* set the actual count of items !)
6663 * PARAMETER(S):
6664 * [I] infoPtr : valid pointer to the listview structure
6665 * [I] INT : item count (projected number of items to allocate)
6666 * [I] DWORD : update flags
6668 * RETURN:
6669 * SUCCESS : TRUE
6670 * FAILURE : FALSE
6672 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6674 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6676 if (infoPtr->dwStyle & LVS_OWNERDATA)
6678 int precount,topvisible;
6680 TRACE("LVS_OWNERDATA is set!\n");
6681 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6682 FIXME("flags %s %s not implemented\n",
6683 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6684 : "",
6685 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6688 * Internally remove all the selections.
6689 * FIXME: why not RemoveAllSelections
6693 RANGE *selection;
6694 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
6695 if (selection)
6696 LISTVIEW_RemoveSelectionRange(infoPtr,selection->lower,
6697 selection->upper);
6699 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
6701 precount = infoPtr->hdpaItems->nItemCount;
6702 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6703 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6705 /* Grow the hdpaItems array if necessary */
6706 if (nItems > infoPtr->hdpaItems->nMaxCount)
6707 if (!DPA_SetPtr(infoPtr->hdpaItems, nItems - 1, NULL))
6708 return FALSE;
6710 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(infoPtr),
6711 DEFAULT_COLUMN_WIDTH);
6713 LISTVIEW_UpdateSize(infoPtr);
6714 LISTVIEW_UpdateScroll(infoPtr);
6716 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
6717 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6719 else
6721 /* According to MSDN for non-LVS_OWNERDATA this is just
6722 * a performance issue. The control allocates its internal
6723 * data structures for the number of items specified. It
6724 * cuts down on the number of memory allocations. Therefore
6725 * we will just issue a WARN here
6727 WARN("for non-ownerdata performance option not implemented.\n");
6730 return TRUE;
6733 /***
6734 * DESCRIPTION:
6735 * Sets the position of an item.
6737 * PARAMETER(S):
6738 * [I] infoPtr : valid pointer to the listview structure
6739 * [I] INT : item index
6740 * [I] LONG : x coordinate
6741 * [I] LONG : y coordinate
6743 * RETURN:
6744 * SUCCESS : TRUE
6745 * FAILURE : FALSE
6747 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem,
6748 LONG nPosX, LONG nPosY)
6750 UINT lStyle = infoPtr->dwStyle;
6751 UINT uView = lStyle & LVS_TYPEMASK;
6752 LISTVIEW_ITEM *lpItem;
6753 HDPA hdpaSubItems;
6754 BOOL bResult = FALSE;
6756 TRACE("(nItem=%d, X=%ld, Y=%ld)\n", nItem, nPosX, nPosY);
6758 if (lStyle & LVS_OWNERDATA)
6759 return FALSE;
6761 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
6763 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
6765 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
6767 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
6769 POINT orig;
6770 bResult = TRUE;
6771 orig = lpItem->ptPosition;
6772 if ((nPosX == -1) && (nPosY == -1))
6774 /* This point value seems to be an undocumented feature. The
6775 * best guess is that it means either at the origin, or at
6776 * the true beginning of the list. I will assume the origin.
6778 POINT pt1;
6779 if (!LISTVIEW_GetOrigin(infoPtr, &pt1))
6781 pt1.x = 0;
6782 pt1.y = 0;
6784 nPosX = pt1.x;
6785 nPosY = pt1.y;
6786 if (uView == LVS_ICON)
6788 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6789 nPosY += ICON_TOP_PADDING;
6791 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
6792 nPosX, nPosY);
6795 lpItem->ptPosition.x = nPosX;
6796 lpItem->ptPosition.y = nPosY;
6797 if (uView == LVS_ICON)
6799 lpItem->ptPosition.y -= ICON_TOP_PADDING;
6800 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6801 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
6803 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
6804 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
6807 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
6808 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
6811 else
6813 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
6814 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
6822 return bResult;
6825 /***
6826 * DESCRIPTION:
6827 * Sets the state of one or many items.
6829 * PARAMETER(S):
6830 * [I] infoPtr : valid pointer to the listview structure
6831 * [I]INT : item index
6832 * [I] LPLVITEM : item or subitem info
6834 * RETURN:
6835 * SUCCESS : TRUE
6836 * FAILURE : FALSE
6838 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6840 BOOL bResult = TRUE;
6841 LVITEMW lvItem;
6843 lvItem.iItem = nItem;
6844 lvItem.iSubItem = 0;
6845 lvItem.mask = LVIF_STATE;
6846 lvItem.state = lpLVItem->state;
6847 lvItem.stateMask = lpLVItem->stateMask;
6848 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6850 if (nItem == -1)
6852 /* apply to all items */
6853 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
6854 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6856 else
6857 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6859 return bResult;
6862 /***
6863 * DESCRIPTION:
6864 * Sets the text of an item or subitem.
6866 * PARAMETER(S):
6867 * [I] hwnd : window handle
6868 * [I] nItem : item index
6869 * [I] lpLVItem : item or subitem info
6870 * [I] isW : TRUE if input is Unicode
6872 * RETURN:
6873 * SUCCESS : TRUE
6874 * FAILURE : FALSE
6876 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6878 LVITEMW lvItem;
6880 if ((nItem < 0) && (nItem >= GETITEMCOUNT(infoPtr))) return FALSE;
6882 lvItem.iItem = nItem;
6883 lvItem.iSubItem = lpLVItem->iSubItem;
6884 lvItem.mask = LVIF_TEXT;
6885 lvItem.pszText = lpLVItem->pszText;
6886 lvItem.cchTextMax = lpLVItem->cchTextMax;
6888 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6890 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6893 /***
6894 * DESCRIPTION:
6895 * Set item index that marks the start of a multiple selection.
6897 * PARAMETER(S):
6898 * [I] infoPtr : valid pointer to the listview structure
6899 * [I] INT : index
6901 * RETURN:
6902 * Index number or -1 if there is no selection mark.
6904 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6906 INT nOldIndex = infoPtr->nSelectionMark;
6908 TRACE("(nIndex=%d)\n", nIndex);
6910 infoPtr->nSelectionMark = nIndex;
6912 return nOldIndex;
6915 /***
6916 * DESCRIPTION:
6917 * Sets the text background color.
6919 * PARAMETER(S):
6920 * [I] infoPtr : valid pointer to the listview structure
6921 * [I] COLORREF : text background color
6923 * RETURN:
6924 * SUCCESS : TRUE
6925 * FAILURE : FALSE
6927 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6929 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6931 if (infoPtr->clrTextBk != clrTextBk)
6933 infoPtr->clrTextBk = clrTextBk;
6934 LISTVIEW_InvalidateList(infoPtr);
6937 return TRUE;
6940 /***
6941 * DESCRIPTION:
6942 * Sets the text foreground color.
6944 * PARAMETER(S):
6945 * [I] infoPtr : valid pointer to the listview structure
6946 * [I] COLORREF : text color
6948 * RETURN:
6949 * SUCCESS : TRUE
6950 * FAILURE : FALSE
6952 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6954 TRACE("(clrText=%lx)\n", clrText);
6956 if (infoPtr->clrText != clrText)
6958 infoPtr->clrText = clrText;
6959 LISTVIEW_InvalidateList(infoPtr);
6962 return TRUE;
6965 /* LISTVIEW_SetToolTips */
6966 /* LISTVIEW_SetUnicodeFormat */
6967 /* LISTVIEW_SetWorkAreas */
6969 /***
6970 * DESCRIPTION:
6971 * Callback internally used by LISTVIEW_SortItems()
6973 * PARAMETER(S):
6974 * [I] LPVOID : first LISTVIEW_ITEM to compare
6975 * [I] LPVOID : second LISTVIEW_ITEM to compare
6976 * [I] LPARAM : HWND of control
6978 * RETURN:
6979 * if first comes before second : negative
6980 * if first comes after second : positive
6981 * if first and second are equivalent : zero
6983 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6985 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6986 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6987 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6989 /* Forward the call to the client defined callback */
6990 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6993 /***
6994 * DESCRIPTION:
6995 * Sorts the listview items.
6997 * PARAMETER(S):
6998 * [I] infoPtr : valid pointer to the listview structure
6999 * [I] WPARAM : application-defined value
7000 * [I] LPARAM : pointer to comparision callback
7002 * RETURN:
7003 * SUCCESS : TRUE
7004 * FAILURE : FALSE
7006 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7008 UINT lStyle = infoPtr->dwStyle;
7009 HDPA hdpaSubItems;
7010 LISTVIEW_ITEM *lpItem;
7011 LPVOID selectionMarkItem;
7012 int i;
7014 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7016 if (lStyle & LVS_OWNERDATA) return FALSE;
7018 if (!infoPtr->hdpaItems) return FALSE;
7020 /* if there are 0 or 1 items, there is no need to sort */
7021 if (GETITEMCOUNT(infoPtr) < 2) return TRUE;
7023 if (infoPtr->nFocusedItem >= 0)
7025 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7026 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7027 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7030 infoPtr->pfnCompare = pfnCompare;
7031 infoPtr->lParamSort = lParamSort;
7032 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
7034 /* Adjust selections and indices so that they are the way they should
7035 * be after the sort (otherwise, the list items move around, but
7036 * whatever is at the item's previous original position will be
7037 * selected instead)
7039 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7040 for (i=0; i < GETITEMCOUNT(infoPtr); i++)
7042 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7043 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7045 if (lpItem->state & LVIS_SELECTED)
7046 LISTVIEW_AddSelectionRange(infoPtr, i, i);
7047 else
7048 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
7049 if (lpItem->state & LVIS_FOCUSED)
7051 infoPtr->nFocusedItem = i;
7052 lpItem->state &= ~LVIS_FOCUSED;
7055 if (selectionMarkItem != NULL)
7056 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7057 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7059 /* align the items */
7060 LISTVIEW_AlignTop(infoPtr);
7062 /* refresh the display */
7063 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
7065 return TRUE;
7068 /***
7069 * DESCRIPTION:
7070 * Updates an items or rearranges the listview control.
7072 * PARAMETER(S):
7073 * [I] infoPtr : valid pointer to the listview structure
7074 * [I] INT : item index
7076 * RETURN:
7077 * SUCCESS : TRUE
7078 * FAILURE : FALSE
7080 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7082 LONG lStyle = infoPtr->dwStyle;
7083 UINT uView = lStyle & LVS_TYPEMASK;
7085 TRACE("(nItem=%d)\n", nItem);
7087 if ((nItem < 0) && (nItem >= GETITEMCOUNT(infoPtr))) return FALSE;
7089 /* rearrange with default alignment style */
7090 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
7091 LISTVIEW_Arrange(infoPtr, 0);
7092 else
7093 LISTVIEW_InvalidateItem(infoPtr, nItem);
7095 return TRUE;
7099 /***
7100 * DESCRIPTION:
7101 * Creates the listview control.
7103 * PARAMETER(S):
7104 * [I] hwnd : window handle
7105 * [I] lpcs : the create parameters
7107 * RETURN:
7108 * Success: 0
7109 * Failure: -1
7111 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7113 LISTVIEW_INFO *infoPtr;
7114 UINT uView = lpcs->style & LVS_TYPEMASK;
7115 LOGFONTW logFont;
7117 TRACE("(lpcs=%p)\n", lpcs);
7119 /* initialize info pointer */
7120 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7121 if (!infoPtr) return -1;
7123 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7125 infoPtr->hwndSelf = hwnd;
7126 infoPtr->dwStyle = lpcs->style;
7127 /* determine the type of structures to use */
7128 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7129 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7131 /* initialize color information */
7132 infoPtr->clrBk = CLR_NONE;
7133 infoPtr->clrText = comctl32_color.clrWindowText;
7134 infoPtr->clrTextBk = CLR_DEFAULT;
7135 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7137 /* set default values */
7138 infoPtr->nFocusedItem = -1;
7139 infoPtr->nSelectionMark = -1;
7140 infoPtr->nHotItem = -1;
7141 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7142 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7143 infoPtr->nEditLabelItem = -1;
7145 /* get default font (icon title) */
7146 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7147 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7148 infoPtr->hFont = infoPtr->hDefaultFont;
7149 LISTVIEW_SaveTextMetrics(infoPtr);
7151 /* create header */
7152 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7153 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7154 0, 0, 0, 0, hwnd, (HMENU)0,
7155 lpcs->hInstance, NULL);
7157 /* set header unicode format */
7158 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
7160 /* set header font */
7161 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7162 (LPARAM)TRUE);
7164 if (uView == LVS_ICON)
7166 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7167 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7169 else if (uView == LVS_REPORT)
7171 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7173 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7175 else
7177 /* set HDS_HIDDEN flag to hide the header bar */
7178 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7179 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7183 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7184 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7186 else
7188 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7189 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7192 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
7193 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
7195 /* display unsupported listview window styles */
7196 LISTVIEW_UnsupportedStyles(lpcs->style);
7198 /* allocate memory for the data structure */
7199 infoPtr->hdpaItems = DPA_Create(10);
7201 /* allocate memory for the selection ranges */
7202 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7204 /* initialize size of items */
7205 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
7206 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7208 /* initialize the hover time to -1(indicating the default system hover time) */
7209 infoPtr->dwHoverTime = -1;
7211 return 0;
7214 /***
7215 * DESCRIPTION:
7216 * Erases the background of the listview control.
7218 * PARAMETER(S):
7219 * [I] infoPtr : valid pointer to the listview structure
7220 * [I] hdc : device context handle
7222 * RETURN:
7223 * SUCCESS : TRUE
7224 * FAILURE : FALSE
7226 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7228 RECT rc;
7230 TRACE("(hdc=%x)\n", hdc);
7232 if (!GetClipBox(hdc, &rc)) return FALSE;
7234 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7238 /***
7239 * DESCRIPTION:
7240 * Helper function for LISTVIEW_[HV]Scroll *only*.
7241 * Performs vertical/horizontal scrolling by a give amount.
7243 * PARAMETER(S):
7244 * [I] infoPtr : valid pointer to the listview structure
7245 * [I] dx : amount of horizontal scroll
7246 * [I] dy : amount of vertical scroll
7248 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7250 /* now we can scroll the list */
7251 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7252 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7253 /* if we have focus, adjust rect */
7254 if (infoPtr->bFocus && !IsRectEmpty(&infoPtr->rcFocus))
7255 OffsetRect(&infoPtr->rcFocus, dx, dy);
7256 UpdateWindow(infoPtr->hwndSelf);
7259 /***
7260 * DESCRIPTION:
7261 * Performs vertical scrolling.
7263 * PARAMETER(S):
7264 * [I] infoPtr : valid pointer to the listview structure
7265 * [I] nScrollCode : scroll code
7266 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7267 * [I] hScrollWnd : scrollbar control window handle
7269 * RETURN:
7270 * Zero
7272 * NOTES:
7273 * SB_LINEUP/SB_LINEDOWN:
7274 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7275 * for LVS_REPORT is 1 line
7276 * for LVS_LIST cannot occur
7279 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7280 INT nScrollDiff, HWND hScrollWnd)
7282 UINT uView = LISTVIEW_GetType(infoPtr);
7283 INT nOldScrollPos, nNewScrollPos;
7284 SCROLLINFO scrollInfo;
7285 BOOL is_an_icon;
7287 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7289 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7291 scrollInfo.cbSize = sizeof(SCROLLINFO);
7292 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7294 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7296 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7298 nOldScrollPos = scrollInfo.nPos;
7299 switch (nScrollCode)
7301 case SB_INTERNAL:
7302 break;
7304 case SB_LINEUP:
7305 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7306 break;
7308 case SB_LINEDOWN:
7309 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7310 break;
7312 case SB_PAGEUP:
7313 nScrollDiff = -scrollInfo.nPage;
7314 break;
7316 case SB_PAGEDOWN:
7317 nScrollDiff = scrollInfo.nPage;
7318 break;
7320 case SB_THUMBPOSITION:
7321 case SB_THUMBTRACK:
7322 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7323 break;
7325 default:
7326 nScrollDiff = 0;
7329 /* quit right away if pos isn't changing */
7330 if (nScrollDiff == 0) return 0;
7332 /* calculate new position, and handle overflows */
7333 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7334 if (nScrollDiff > 0) {
7335 if (nNewScrollPos < nOldScrollPos ||
7336 nNewScrollPos > scrollInfo.nMax)
7337 nNewScrollPos = scrollInfo.nMax;
7338 } else {
7339 if (nNewScrollPos > nOldScrollPos ||
7340 nNewScrollPos < scrollInfo.nMin)
7341 nNewScrollPos = scrollInfo.nMin;
7344 /* set the new position, and reread in case it changed */
7345 scrollInfo.fMask = SIF_POS;
7346 scrollInfo.nPos = nNewScrollPos;
7347 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7349 /* carry on only if it really changed */
7350 if (nNewScrollPos == nOldScrollPos) return 0;
7352 /* now adjust to client coordinates */
7353 nScrollDiff = nOldScrollPos - nNewScrollPos;
7354 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7356 /* and scroll the window */
7357 scroll_list(infoPtr, 0, nScrollDiff);
7359 return 0;
7362 /***
7363 * DESCRIPTION:
7364 * Performs horizontal scrolling.
7366 * PARAMETER(S):
7367 * [I] infoPtr : valid pointer to the listview structure
7368 * [I] nScrollCode : scroll code
7369 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7370 * [I] hScrollWnd : scrollbar control window handle
7372 * RETURN:
7373 * Zero
7375 * NOTES:
7376 * SB_LINELEFT/SB_LINERIGHT:
7377 * for LVS_ICON, LVS_SMALLICON 1 pixel
7378 * for LVS_REPORT is 1 pixel
7379 * for LVS_LIST is 1 column --> which is a 1 because the
7380 * scroll is based on columns not pixels
7383 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7384 INT nScrollDiff, HWND hScrollWnd)
7386 UINT uView = LISTVIEW_GetType(infoPtr);
7387 INT nOldScrollPos, nNewScrollPos;
7388 SCROLLINFO scrollInfo;
7390 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7392 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7394 scrollInfo.cbSize = sizeof(SCROLLINFO);
7395 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7397 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7399 nOldScrollPos = scrollInfo.nPos;
7401 switch (nScrollCode)
7403 case SB_INTERNAL:
7404 break;
7406 case SB_LINELEFT:
7407 nScrollDiff = -1;
7408 break;
7410 case SB_LINERIGHT:
7411 nScrollDiff = 1;
7412 break;
7414 case SB_PAGELEFT:
7415 nScrollDiff = -scrollInfo.nPage;
7416 break;
7418 case SB_PAGERIGHT:
7419 nScrollDiff = scrollInfo.nPage;
7420 break;
7422 case SB_THUMBPOSITION:
7423 case SB_THUMBTRACK:
7424 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7425 break;
7427 default:
7428 nScrollDiff = 0;
7431 /* quit right away if pos isn't changing */
7432 if (nScrollDiff == 0) return 0;
7434 /* calculate new position, and handle overflows */
7435 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7436 if (nScrollDiff > 0) {
7437 if (nNewScrollPos < nOldScrollPos ||
7438 nNewScrollPos > scrollInfo.nMax)
7439 nNewScrollPos = scrollInfo.nMax;
7440 } else {
7441 if (nNewScrollPos > nOldScrollPos ||
7442 nNewScrollPos < scrollInfo.nMin)
7443 nNewScrollPos = scrollInfo.nMin;
7446 /* set the new position, and reread in case it changed */
7447 scrollInfo.fMask = SIF_POS;
7448 scrollInfo.nPos = nNewScrollPos;
7449 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7451 /* carry on only if it really changed */
7452 if (nNewScrollPos == nOldScrollPos) return 0;
7454 if(uView == LVS_REPORT)
7455 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7457 /* now adjust to client coordinates */
7458 nScrollDiff = nOldScrollPos - nNewScrollPos;
7459 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7461 /* and scroll the window */
7462 scroll_list(infoPtr, nScrollDiff, 0);
7464 return 0;
7467 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7469 UINT uView = LISTVIEW_GetType(infoPtr);
7470 INT gcWheelDelta = 0;
7471 UINT pulScrollLines = 3;
7472 SCROLLINFO scrollInfo;
7474 TRACE("(wheelDelta=%d)\n", wheelDelta);
7476 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7477 gcWheelDelta -= wheelDelta;
7479 scrollInfo.cbSize = sizeof(SCROLLINFO);
7480 scrollInfo.fMask = SIF_POS;
7482 switch(uView)
7484 case LVS_ICON:
7485 case LVS_SMALLICON:
7487 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7488 * should be fixed in the future.
7490 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7491 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7492 scrollInfo.nPos + (gcWheelDelta < 0) ?
7493 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7494 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7495 break;
7497 case LVS_REPORT:
7498 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7500 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7502 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7503 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7504 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7507 break;
7509 case LVS_LIST:
7510 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7511 break;
7513 return 0;
7516 /***
7517 * DESCRIPTION:
7518 * ???
7520 * PARAMETER(S):
7521 * [I] infoPtr : valid pointer to the listview structure
7522 * [I] INT : virtual key
7523 * [I] LONG : key data
7525 * RETURN:
7526 * Zero
7528 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7530 UINT uView = LISTVIEW_GetType(infoPtr);
7531 INT nItem = -1;
7532 NMLVKEYDOWN nmKeyDown;
7534 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7536 /* send LVN_KEYDOWN notification */
7537 nmKeyDown.wVKey = nVirtualKey;
7538 nmKeyDown.flags = 0;
7539 notify(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7541 switch (nVirtualKey)
7543 case VK_RETURN:
7544 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7546 notify_return(infoPtr);
7547 notify_itemactivate(infoPtr);
7549 break;
7551 case VK_HOME:
7552 if (GETITEMCOUNT(infoPtr) > 0)
7553 nItem = 0;
7554 break;
7556 case VK_END:
7557 if (GETITEMCOUNT(infoPtr) > 0)
7558 nItem = GETITEMCOUNT(infoPtr) - 1;
7559 break;
7561 case VK_LEFT:
7562 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7563 break;
7565 case VK_UP:
7566 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7567 break;
7569 case VK_RIGHT:
7570 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7571 break;
7573 case VK_DOWN:
7574 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7575 break;
7577 case VK_PRIOR:
7578 if (uView == LVS_REPORT)
7579 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7580 else
7581 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7582 * LISTVIEW_GetCountPerRow(infoPtr);
7583 if(nItem < 0) nItem = 0;
7584 break;
7586 case VK_NEXT:
7587 if (uView == LVS_REPORT)
7588 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7589 else
7590 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7591 * LISTVIEW_GetCountPerRow(infoPtr);
7592 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
7593 break;
7596 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7597 LISTVIEW_KeySelection(infoPtr, nItem);
7599 return 0;
7602 /***
7603 * DESCRIPTION:
7604 * Kills the focus.
7606 * PARAMETER(S):
7607 * [I] infoPtr : valid pointer to the listview structure
7609 * RETURN:
7610 * Zero
7612 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7614 TRACE("()\n");
7616 /* if we did not have the focus, there's nothing to do */
7617 if (!infoPtr->bFocus) return 0;
7619 /* send NM_KILLFOCUS notification */
7620 notify_killfocus(infoPtr);
7622 /* if we have a focus rectagle, get rid of it */
7623 LISTVIEW_ToggleFocusRect(infoPtr);
7625 /* invalidate the selected items before reseting focus flag */
7626 LISTVIEW_InvalidateSelectedItems(infoPtr);
7628 /* set window focus flag */
7629 infoPtr->bFocus = FALSE;
7631 return 0;
7634 /***
7635 * DESCRIPTION:
7636 * Processes double click 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_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7648 LVHITTESTINFO htInfo;
7649 NMLISTVIEW nmlv;
7651 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7653 htInfo.pt.x = pts.x;
7654 htInfo.pt.y = pts.y;
7656 /* send NM_DBLCLK notification */
7657 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7658 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE);
7659 nmlv.iItem = htInfo.iItem;
7660 nmlv.iSubItem = htInfo.iSubItem;
7661 nmlv.ptAction = htInfo.pt;
7662 notify_listview(infoPtr, NM_DBLCLK, &nmlv);
7664 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7665 if(nmlv.iItem != -1)
7666 notify_itemactivate(infoPtr);
7668 return 0;
7671 /***
7672 * DESCRIPTION:
7673 * Processes mouse down messages (left mouse button).
7675 * PARAMETER(S):
7676 * [I] infoPtr : valid pointer to the listview structure
7677 * [I] wKey : key flag
7678 * [I] pts : mouse coordinate
7680 * RETURN:
7681 * Zero
7683 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7685 LONG lStyle = infoPtr->dwStyle;
7686 static BOOL bGroupSelect = TRUE;
7687 POINT pt = { pts.x, pts.y };
7688 INT nItem;
7690 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7692 /* FIXME: NM_CLICK */
7694 /* send NM_RELEASEDCAPTURE notification */
7695 notify_releasedcapture(infoPtr);
7697 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7699 /* set left button down flag */
7700 infoPtr->bLButtonDown = TRUE;
7702 nItem = LISTVIEW_GetItemAtPt(infoPtr, pt);
7703 TRACE("nItem=%d\n", nItem);
7704 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7706 if (lStyle & LVS_SINGLESEL)
7708 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7709 && infoPtr->nEditLabelItem == -1)
7710 infoPtr->nEditLabelItem = nItem;
7711 else
7712 LISTVIEW_SetSelection(infoPtr, nItem);
7714 else
7716 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7718 if (bGroupSelect)
7719 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7720 else
7722 LVITEMW item;
7724 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7725 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7727 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7728 infoPtr->nSelectionMark = nItem;
7731 else if (wKey & MK_CONTROL)
7733 LVITEMW item;
7735 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7737 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7738 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7739 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7740 infoPtr->nSelectionMark = nItem;
7742 else if (wKey & MK_SHIFT)
7744 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7746 else
7748 BOOL was_selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
7750 /* set selection (clears other pre-existing selections) */
7751 LISTVIEW_SetSelection(infoPtr, nItem);
7753 if (was_selected && infoPtr->nEditLabelItem == -1)
7754 infoPtr->nEditLabelItem = nItem;
7758 else
7760 /* remove all selections */
7761 LISTVIEW_RemoveAllSelections(infoPtr);
7764 return 0;
7767 /***
7768 * DESCRIPTION:
7769 * Processes mouse up messages (left mouse button).
7771 * PARAMETER(S):
7772 * [I] infoPtr : valid pointer to the listview structure
7773 * [I] wKey : key flag
7774 * [I] pts : mouse coordinate
7776 * RETURN:
7777 * Zero
7779 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7781 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7783 if (infoPtr->bLButtonDown)
7785 LVHITTESTINFO lvHitTestInfo;
7786 NMLISTVIEW nmlv;
7788 lvHitTestInfo.pt.x = pts.x;
7789 lvHitTestInfo.pt.y = pts.y;
7791 /* send NM_CLICK notification */
7792 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7793 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE) != -1)
7795 nmlv.iItem = lvHitTestInfo.iItem;
7796 nmlv.iSubItem = lvHitTestInfo.iSubItem;
7798 else
7800 nmlv.iItem = -1;
7801 nmlv.iSubItem = 0;
7803 nmlv.ptAction.x = pts.x;
7804 nmlv.ptAction.y = pts.y;
7805 notify_listview(infoPtr, NM_CLICK, &nmlv);
7807 /* set left button flag */
7808 infoPtr->bLButtonDown = FALSE;
7810 if(infoPtr->nEditLabelItem != -1)
7812 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL) {
7813 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7815 infoPtr->nEditLabelItem = -1;
7819 return 0;
7822 /***
7823 * DESCRIPTION:
7824 * Destroys the listview control (called after WM_DESTROY).
7826 * PARAMETER(S):
7827 * [I] infoPtr : valid pointer to the listview structure
7829 * RETURN:
7830 * Zero
7832 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7834 LONG lStyle = infoPtr->dwStyle;
7836 TRACE("()\n");
7838 /* delete all items */
7839 LISTVIEW_DeleteAllItems(infoPtr);
7841 /* destroy data structure */
7842 DPA_Destroy(infoPtr->hdpaItems);
7843 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7845 /* destroy image lists */
7846 if (!(lStyle & LVS_SHAREIMAGELISTS))
7848 /* FIXME: If the caller does a ImageList_Destroy and then we
7849 * do this code the area will be freed twice. Currently
7850 * this generates an "err:heap:HEAP_ValidateInUseArena
7851 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7852 * has PREV_FREE flag" sometimes.
7854 * We will leak the memory till we figure out how to fix
7856 if (infoPtr->himlNormal)
7857 ImageList_Destroy(infoPtr->himlNormal);
7858 if (infoPtr->himlSmall)
7859 ImageList_Destroy(infoPtr->himlSmall);
7860 if (infoPtr->himlState)
7861 ImageList_Destroy(infoPtr->himlState);
7864 /* destroy font, bkgnd brush */
7865 infoPtr->hFont = 0;
7866 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7867 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7869 /* free listview info pointer*/
7870 COMCTL32_Free(infoPtr);
7872 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7873 return 0;
7876 /***
7877 * DESCRIPTION:
7878 * Handles notifications from children.
7880 * PARAMETER(S):
7881 * [I] infoPtr : valid pointer to the listview structure
7882 * [I] INT : control identifier
7883 * [I] LPNMHDR : notification information
7885 * RETURN:
7886 * Zero
7888 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7890 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7892 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7894 /* handle notification from header control */
7895 if (lpnmh->code == HDN_ENDTRACKW)
7897 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
7898 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7900 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7902 /* Handle sorting by Header Column */
7903 NMLISTVIEW nmlv;
7905 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7906 nmlv.iItem = -1;
7907 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7908 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7910 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7912 /* Idealy this should be done in HDN_ENDTRACKA
7913 * but since SetItemBounds in Header.c is called after
7914 * the notification is sent, it is neccessary to handle the
7915 * update of the scroll bar here (Header.c works fine as it is,
7916 * no need to disturb it)
7918 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
7919 LISTVIEW_UpdateScroll(infoPtr);
7920 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7925 return 0;
7928 /***
7929 * DESCRIPTION:
7930 * Determines the type of structure to use.
7932 * PARAMETER(S):
7933 * [I] infoPtr : valid pointer to the listview structureof the sender
7934 * [I] HWND : listview window handle
7935 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7937 * RETURN:
7938 * Zero
7940 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7942 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7944 if (nCommand == NF_REQUERY)
7945 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7946 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7947 return 0;
7950 /***
7951 * DESCRIPTION:
7952 * Paints/Repaints the listview control.
7954 * PARAMETER(S):
7955 * [I] infoPtr : valid pointer to the listview structure
7956 * [I] HDC : device context handle
7958 * RETURN:
7959 * Zero
7961 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7963 TRACE("(hdc=%x)\n", hdc);
7965 if (hdc)
7966 LISTVIEW_Refresh(infoPtr, hdc);
7967 else
7969 PAINTSTRUCT ps;
7971 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7972 if (!hdc) return 1;
7973 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7974 LISTVIEW_Refresh(infoPtr, hdc);
7975 EndPaint(infoPtr->hwndSelf, &ps);
7978 return 0;
7981 /***
7982 * DESCRIPTION:
7983 * Processes double click messages (right mouse button).
7985 * PARAMETER(S):
7986 * [I] infoPtr : valid pointer to the listview structure
7987 * [I] wKey : key flag
7988 * [I] pts : mouse coordinate
7990 * RETURN:
7991 * Zero
7993 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7995 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7997 /* send NM_RELEASEDCAPTURE notification */
7998 notify_releasedcapture(infoPtr);
8000 /* send NM_RDBLCLK notification */
8001 notify_rdblclk(infoPtr);
8003 return 0;
8006 /***
8007 * DESCRIPTION:
8008 * Processes mouse down messages (right mouse button).
8010 * PARAMETER(S):
8011 * [I] infoPtr : valid pointer to the listview structure
8012 * [I] wKey : key flag
8013 * [I] pts : mouse coordinate
8015 * RETURN:
8016 * Zero
8018 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8020 LVHITTESTINFO lvHitTestInfo;
8021 NMLISTVIEW nmlv;
8022 INT nItem;
8024 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8026 /* FIXME: NM_CLICK */
8028 /* send NM_RELEASEDCAPTURE notification */
8029 notify_releasedcapture(infoPtr);
8031 /* make sure the listview control window has the focus */
8032 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8034 /* set right button down flag */
8035 infoPtr->bRButtonDown = TRUE;
8037 /* determine the index of the selected item */
8038 lvHitTestInfo.pt.x = pts.x;
8039 lvHitTestInfo.pt.y = pts.y;
8040 nItem = LISTVIEW_GetItemAtPt(infoPtr, lvHitTestInfo.pt);
8042 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8044 LISTVIEW_SetItemFocus(infoPtr,nItem);
8045 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8046 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8047 LISTVIEW_SetSelection(infoPtr, nItem);
8049 else
8051 LISTVIEW_RemoveAllSelections(infoPtr);
8055 /* Send NM_RClICK notification */
8056 ZeroMemory(&nmlv, sizeof(nmlv));
8057 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE);
8058 nmlv.iItem = lvHitTestInfo.iItem;
8059 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8060 nmlv.ptAction = lvHitTestInfo.pt;
8061 notify_listview(infoPtr, NM_RCLICK, &nmlv);
8063 return 0;
8066 /***
8067 * DESCRIPTION:
8068 * Processes mouse up messages (right mouse button).
8070 * PARAMETER(S):
8071 * [I] infoPtr : valid pointer to the listview structure
8072 * [I] wKey : key flag
8073 * [I] pts : mouse coordinate
8075 * RETURN:
8076 * Zero
8078 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8080 POINT pt = { pts.x, pts.y };
8082 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8084 if (!infoPtr->bRButtonDown) return 0;
8086 /* set button flag */
8087 infoPtr->bRButtonDown = FALSE;
8089 /* Change to screen coordinate for WM_CONTEXTMENU */
8090 ClientToScreen(infoPtr->hwndSelf, &pt);
8092 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8093 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8094 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8096 return 0;
8100 /***
8101 * DESCRIPTION:
8102 * Sets the cursor.
8104 * PARAMETER(S):
8105 * [I] infoPtr : valid pointer to the listview structure
8106 * [I] hwnd : window handle of window containing the cursor
8107 * [I] nHittest : hit-test code
8108 * [I] wMouseMsg : ideintifier of the mouse message
8110 * RETURN:
8111 * TRUE if cursor is set
8112 * FALSE otherwise
8114 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8116 POINT pt;
8118 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8120 if(!infoPtr->hHotCursor) return FALSE;
8122 GetCursorPos(&pt);
8123 if (LISTVIEW_GetItemAtPt(infoPtr, pt) < 0) return FALSE;
8125 SetCursor(infoPtr->hHotCursor);
8127 return TRUE;
8130 /***
8131 * DESCRIPTION:
8132 * Sets the focus.
8134 * PARAMETER(S):
8135 * [I] infoPtr : valid pointer to the listview structure
8136 * [I] infoPtr : handle of previously focused window
8138 * RETURN:
8139 * Zero
8141 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8143 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
8145 /* if we have the focus already, there's nothing to do */
8146 if (infoPtr->bFocus) return 0;
8148 /* send NM_SETFOCUS notification */
8149 notify_setfocus(infoPtr);
8151 /* put the focus rect back on */
8152 LISTVIEW_ToggleFocusRect(infoPtr);
8154 /* set window focus flag */
8155 infoPtr->bFocus = TRUE;
8157 /* redraw all visible selected items */
8158 LISTVIEW_InvalidateSelectedItems(infoPtr);
8160 return 0;
8163 /***
8164 * DESCRIPTION:
8165 * Sets the font.
8167 * PARAMETER(S):
8168 * [I] infoPtr : valid pointer to the listview structure
8169 * [I] HFONT : font handle
8170 * [I] WORD : redraw flag
8172 * RETURN:
8173 * Zero
8175 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8177 HFONT oldFont = infoPtr->hFont;
8179 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
8181 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8182 if (infoPtr->hFont == oldFont) return 0;
8184 LISTVIEW_SaveTextMetrics(infoPtr);
8186 if (LISTVIEW_GetType(infoPtr) == LVS_REPORT)
8187 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8189 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8191 return 0;
8194 /***
8195 * DESCRIPTION:
8196 * Message handling for WM_SETREDRAW.
8197 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8199 * PARAMETER(S):
8200 * [I] infoPtr : valid pointer to the listview structure
8201 * [I] bRedraw: state of redraw flag
8203 * RETURN:
8204 * DefWinProc return value
8206 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8208 /* FIXME: this is bogus */
8209 LRESULT lResult = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, bRedraw, 0);
8210 if(bRedraw)
8211 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8212 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8213 return lResult;
8216 /***
8217 * DESCRIPTION:
8218 * Resizes the listview control. This function processes WM_SIZE
8219 * messages. At this time, the width and height are not used.
8221 * PARAMETER(S):
8222 * [I] infoPtr : valid pointer to the listview structure
8223 * [I] WORD : new width
8224 * [I] WORD : new height
8226 * RETURN:
8227 * Zero
8229 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8231 LONG lStyle = infoPtr->dwStyle;
8232 UINT uView = lStyle & LVS_TYPEMASK;
8234 TRACE("(width=%d, height=%d)\n", Width, Height);
8236 if (LISTVIEW_UpdateSize(infoPtr))
8238 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8240 if (lStyle & LVS_ALIGNLEFT)
8241 LISTVIEW_AlignLeft(infoPtr);
8242 else
8243 LISTVIEW_AlignTop(infoPtr);
8246 LISTVIEW_UpdateScroll(infoPtr);
8248 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8251 return 0;
8254 /***
8255 * DESCRIPTION:
8256 * Sets the size information.
8258 * PARAMETER(S):
8259 * [I] infoPtr : valid pointer to the listview structure
8261 * RETURN:
8262 * Zero if no size change
8263 * 1 of size changed
8265 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8267 LONG lStyle = infoPtr->dwStyle;
8268 UINT uView = lStyle & LVS_TYPEMASK;
8269 RECT rcList;
8270 RECT rcOld;
8272 GetClientRect(infoPtr->hwndSelf, &rcList);
8273 CopyRect(&rcOld,&(infoPtr->rcList));
8274 infoPtr->rcList.left = 0;
8275 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8276 infoPtr->rcList.top = 0;
8277 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8279 if (uView == LVS_LIST)
8281 /* Apparently the "LIST" style is supposed to have the same
8282 * number of items in a column even if there is no scroll bar.
8283 * Since if a scroll bar already exists then the bottom is already
8284 * reduced, only reduce if the scroll bar does not currently exist.
8285 * The "2" is there to mimic the native control. I think it may be
8286 * related to either padding or edges. (GLA 7/2002)
8288 if (!(lStyle & WS_HSCROLL))
8290 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8291 if (infoPtr->rcList.bottom > nHScrollHeight)
8292 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8294 else
8296 if (infoPtr->rcList.bottom > 2)
8297 infoPtr->rcList.bottom -= 2;
8300 else if (uView == LVS_REPORT)
8302 HDLAYOUT hl;
8303 WINDOWPOS wp;
8305 hl.prc = &rcList;
8306 hl.pwpos = &wp;
8307 Header_Layout(infoPtr->hwndHeader, &hl);
8309 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8311 if (!(LVS_NOCOLUMNHEADER & lStyle))
8312 infoPtr->rcList.top = max(wp.cy, 0);
8314 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8317 /***
8318 * DESCRIPTION:
8319 * Processes WM_STYLECHANGED messages.
8321 * PARAMETER(S):
8322 * [I] infoPtr : valid pointer to the listview structure
8323 * [I] WPARAM : window style type (normal or extended)
8324 * [I] LPSTYLESTRUCT : window style information
8326 * RETURN:
8327 * Zero
8329 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8330 LPSTYLESTRUCT lpss)
8332 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8333 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8334 RECT rcList = infoPtr->rcList;
8336 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8337 wStyleType, lpss->styleOld, lpss->styleNew);
8339 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8341 if (wStyleType == GWL_STYLE)
8343 infoPtr->dwStyle = lpss->styleNew;
8345 if (uOldView == LVS_REPORT)
8346 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8348 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8349 ((lpss->styleNew & WS_HSCROLL) == 0))
8350 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8352 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8353 ((lpss->styleNew & WS_VSCROLL) == 0))
8354 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8356 /* If switching modes, then start with no scroll bars and then
8357 * decide.
8359 if (uNewView != uOldView)
8360 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8362 if (uNewView == LVS_ICON)
8364 INT oldcx, oldcy;
8366 /* First readjust the iconSize and if necessary the iconSpacing */
8367 oldcx = infoPtr->iconSize.cx;
8368 oldcy = infoPtr->iconSize.cy;
8369 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8370 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8371 if (infoPtr->himlNormal != NULL)
8373 INT cx, cy;
8374 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8375 infoPtr->iconSize.cx = cx;
8376 infoPtr->iconSize.cy = cy;
8378 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8380 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8381 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8382 LISTVIEW_SetIconSpacing(infoPtr,0);
8385 /* Now update the full item width and height */
8386 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8387 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8388 if (lpss->styleNew & LVS_ALIGNLEFT)
8389 LISTVIEW_AlignLeft(infoPtr);
8390 else
8391 LISTVIEW_AlignTop(infoPtr);
8393 else if (uNewView == LVS_REPORT)
8395 HDLAYOUT hl;
8396 WINDOWPOS wp;
8398 hl.prc = &rcList;
8399 hl.pwpos = &wp;
8400 Header_Layout(infoPtr->hwndHeader, &hl);
8401 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8402 wp.flags);
8403 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8404 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8406 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8407 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8408 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8409 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8411 else if (uNewView == LVS_LIST)
8413 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8414 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8415 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8416 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8418 else
8420 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8421 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8422 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8423 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8424 if (lpss->styleNew & LVS_ALIGNLEFT)
8425 LISTVIEW_AlignLeft(infoPtr);
8426 else
8427 LISTVIEW_AlignTop(infoPtr);
8430 /* update the size of the client area */
8431 LISTVIEW_UpdateSize(infoPtr);
8433 /* add scrollbars if needed */
8434 LISTVIEW_UpdateScroll(infoPtr);
8436 /* invalidate client area + erase background */
8437 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8439 /* print the list of unsupported window styles */
8440 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8443 /* If they change the view and we have an active edit control
8444 we will need to kill the control since the redraw will
8445 misplace the edit control.
8447 if (infoPtr->bEditing &&
8448 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8449 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8451 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8454 return 0;
8457 /***
8458 * DESCRIPTION:
8459 * Window procedure of the listview control.
8462 static LRESULT WINAPI
8463 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8465 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8467 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8469 if (!infoPtr && (uMsg != WM_CREATE))
8470 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8472 if (infoPtr)
8474 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8477 switch (uMsg)
8479 case LVM_APPROXIMATEVIEWRECT:
8480 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8481 LOWORD(lParam), HIWORD(lParam));
8482 case LVM_ARRANGE:
8483 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8485 /* case LVN_CANCELEDITLABEL */
8487 /* case LVM_CREATEDRAGIMAGE: */
8489 case LVM_DELETEALLITEMS:
8490 return LISTVIEW_DeleteAllItems(infoPtr);
8492 case LVM_DELETECOLUMN:
8493 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8495 case LVM_DELETEITEM:
8496 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8498 case LVM_EDITLABELW:
8499 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8501 case LVM_EDITLABELA:
8502 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8504 /* case LVN_ENABLEGROUPVIEW: */
8506 case LVM_ENSUREVISIBLE:
8507 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8509 case LVM_FINDITEMW:
8510 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8512 case LVM_FINDITEMA:
8513 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8515 case LVM_GETBKCOLOR:
8516 return infoPtr->clrBk;
8518 /* case LVM_GETBKIMAGE: */
8520 case LVM_GETCALLBACKMASK:
8521 return infoPtr->uCallbackMask;
8523 case LVM_GETCOLUMNA:
8524 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8526 case LVM_GETCOLUMNW:
8527 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8529 case LVM_GETCOLUMNORDERARRAY:
8530 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8532 case LVM_GETCOLUMNWIDTH:
8533 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8535 case LVM_GETCOUNTPERPAGE:
8536 return LISTVIEW_GetCountPerPage(infoPtr);
8538 case LVM_GETEDITCONTROL:
8539 return (LRESULT)infoPtr->hwndEdit;
8541 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8542 return infoPtr->dwLvExStyle;
8544 case LVM_GETHEADER:
8545 return (LRESULT)infoPtr->hwndHeader;
8547 case LVM_GETHOTCURSOR:
8548 return infoPtr->hHotCursor;
8550 case LVM_GETHOTITEM:
8551 return infoPtr->nHotItem;
8553 case LVM_GETHOVERTIME:
8554 return infoPtr->dwHoverTime;
8556 case LVM_GETIMAGELIST:
8557 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8559 /* case LVN_GETINSERTMARK: */
8561 /* case LVN_GETINSERTMARKCOLOR: */
8563 /* case LVN_GETINSERTMARKRECT: */
8565 case LVM_GETISEARCHSTRINGA:
8566 case LVM_GETISEARCHSTRINGW:
8567 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8568 return FALSE;
8570 case LVM_GETITEMA:
8571 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8573 case LVM_GETITEMW:
8574 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8576 case LVM_GETITEMCOUNT:
8577 return GETITEMCOUNT(infoPtr);
8579 case LVM_GETITEMPOSITION:
8580 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8582 case LVM_GETITEMRECT:
8583 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8585 case LVM_GETITEMSPACING:
8586 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8588 case LVM_GETITEMSTATE:
8589 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8591 case LVM_GETITEMTEXTA:
8592 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8594 case LVM_GETITEMTEXTW:
8595 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8597 case LVM_GETNEXTITEM:
8598 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8600 case LVM_GETNUMBEROFWORKAREAS:
8601 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8602 return 1;
8604 case LVM_GETORIGIN:
8605 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8607 /* case LVN_GETOUTLINECOLOR: */
8609 /* case LVM_GETSELECTEDCOLUMN: */
8611 case LVM_GETSELECTEDCOUNT:
8612 return LISTVIEW_GetSelectedCount(infoPtr);
8614 case LVM_GETSELECTIONMARK:
8615 return infoPtr->nSelectionMark;
8617 case LVM_GETSTRINGWIDTHA:
8618 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8620 case LVM_GETSTRINGWIDTHW:
8621 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8623 case LVM_GETSUBITEMRECT:
8624 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, ((LPRECT)lParam)->top,
8625 ((LPRECT)lParam)->left, (LPRECT)lParam);
8627 case LVM_GETTEXTBKCOLOR:
8628 return infoPtr->clrTextBk;
8630 case LVM_GETTEXTCOLOR:
8631 return infoPtr->clrText;
8633 /* case LVN_GETTILEINFO: */
8635 /* case LVN_GETTILEVIEWINFO: */
8637 case LVM_GETTOOLTIPS:
8638 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8639 return FALSE;
8641 case LVM_GETTOPINDEX:
8642 return LISTVIEW_GetTopIndex(infoPtr);
8644 /*case LVM_GETUNICODEFORMAT:
8645 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8646 return FALSE;*/
8648 case LVM_GETVIEWRECT:
8649 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8651 case LVM_GETWORKAREAS:
8652 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8653 return FALSE;
8655 /* case LVN_HASGROUP: */
8657 case LVM_HITTEST:
8658 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE);
8660 case LVM_INSERTCOLUMNA:
8661 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8663 case LVM_INSERTCOLUMNW:
8664 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8666 /* case LVN_INSERTGROUP: */
8668 /* case LVN_INSERTGROUPSORTED: */
8670 case LVM_INSERTITEMA:
8671 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8673 case LVM_INSERTITEMW:
8674 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8676 /* case LVN_INSERTMARKHITTEST: */
8678 /* case LVN_ISGROUPVIEWENABLED: */
8680 /* case LVN_MAPIDTOINDEX: */
8682 /* case LVN_INEDXTOID: */
8684 /* case LVN_MOVEGROUP: */
8686 /* case LVN_MOVEITEMTOGROUP: */
8688 case LVM_REDRAWITEMS:
8689 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8691 /* case LVN_REMOVEALLGROUPS: */
8693 /* case LVN_REMOVEGROUP: */
8695 case LVM_SCROLL:
8696 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8698 case LVM_SETBKCOLOR:
8699 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8701 /* case LVM_SETBKIMAGE: */
8703 case LVM_SETCALLBACKMASK:
8704 infoPtr->uCallbackMask = (UINT)wParam;
8705 return TRUE;
8707 case LVM_SETCOLUMNA:
8708 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8710 case LVM_SETCOLUMNW:
8711 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8713 case LVM_SETCOLUMNORDERARRAY:
8714 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8716 case LVM_SETCOLUMNWIDTH:
8717 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8719 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8720 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8722 /* case LVN_SETGROUPINFO: */
8724 /* case LVN_SETGROUPMETRICS: */
8726 case LVM_SETHOTCURSOR:
8727 return LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8729 case LVM_SETHOTITEM:
8730 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8732 case LVM_SETHOVERTIME:
8733 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8735 case LVM_SETICONSPACING:
8736 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8738 case LVM_SETIMAGELIST:
8739 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8741 /* case LVN_SETINFOTIP: */
8743 /* case LVN_SETINSERTMARK: */
8745 /* case LVN_SETINSERTMARKCOLOR: */
8747 case LVM_SETITEMA:
8748 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8750 case LVM_SETITEMW:
8751 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8753 case LVM_SETITEMCOUNT:
8754 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8756 case LVM_SETITEMPOSITION:
8757 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (INT)LOWORD(lParam),
8758 (INT)HIWORD(lParam));
8760 case LVM_SETITEMPOSITION32:
8761 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, ((POINT*)lParam)->x,
8762 ((POINT*)lParam)->y);
8764 case LVM_SETITEMSTATE:
8765 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8767 case LVM_SETITEMTEXTA:
8768 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8770 case LVM_SETITEMTEXTW:
8771 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8773 /* case LVN_SETOUTLINECOLOR: */
8775 /* case LVN_SETSELECTEDCOLUMN: */
8777 case LVM_SETSELECTIONMARK:
8778 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8780 case LVM_SETTEXTBKCOLOR:
8781 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8783 case LVM_SETTEXTCOLOR:
8784 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8786 /* case LVN_SETTILEINFO: */
8788 /* case LVN_SETTILEVIEWINFO: */
8790 /* case LVN_SETTILEWIDTH: */
8792 /* case LVM_SETTOOLTIPS: */
8794 /* case LVM_SETUNICODEFORMAT: */
8796 /* case LVN_SETVIEW: */
8798 /* case LVM_SETWORKAREAS: */
8800 /* case LVN_SORTGROUPS: */
8802 case LVM_SORTITEMS:
8803 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8805 case LVM_SUBITEMHITTEST:
8806 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE);
8808 case LVM_UPDATE:
8809 return LISTVIEW_Update(infoPtr, (INT)wParam);
8811 case WM_CHAR:
8812 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8814 case WM_COMMAND:
8815 return LISTVIEW_Command(infoPtr, wParam, lParam);
8817 case WM_CREATE:
8818 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8820 case WM_ERASEBKGND:
8821 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8823 case WM_GETDLGCODE:
8824 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8826 case WM_GETFONT:
8827 return infoPtr->hFont;
8829 case WM_HSCROLL:
8830 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8832 case WM_KEYDOWN:
8833 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8835 case WM_KILLFOCUS:
8836 return LISTVIEW_KillFocus(infoPtr);
8838 case WM_LBUTTONDBLCLK:
8839 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8841 case WM_LBUTTONDOWN:
8842 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8844 case WM_LBUTTONUP:
8845 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8847 case WM_MOUSEMOVE:
8848 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8850 case WM_MOUSEHOVER:
8851 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8853 case WM_NCDESTROY:
8854 return LISTVIEW_NCDestroy(infoPtr);
8856 case WM_NOTIFY:
8857 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8859 case WM_NOTIFYFORMAT:
8860 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8862 case WM_PAINT:
8863 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8865 case WM_RBUTTONDBLCLK:
8866 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8868 case WM_RBUTTONDOWN:
8869 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8871 case WM_RBUTTONUP:
8872 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8874 case WM_SETCURSOR:
8875 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8876 return TRUE;
8877 goto fwd_msg;
8879 case WM_SETFOCUS:
8880 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8882 case WM_SETFONT:
8883 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8885 case WM_SETREDRAW:
8886 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8888 case WM_SIZE:
8889 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8891 case WM_STYLECHANGED:
8892 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8894 case WM_SYSCOLORCHANGE:
8895 COMCTL32_RefreshSysColors();
8896 return 0;
8898 /* case WM_TIMER: */
8900 case WM_VSCROLL:
8901 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8903 case WM_MOUSEWHEEL:
8904 if (wParam & (MK_SHIFT | MK_CONTROL))
8905 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8906 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8908 case WM_WINDOWPOSCHANGED:
8909 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8910 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8911 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8912 LISTVIEW_UpdateSize(infoPtr);
8913 LISTVIEW_UpdateScroll(infoPtr);
8915 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8917 /* case WM_WININICHANGE: */
8919 default:
8920 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8921 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8923 fwd_msg:
8924 /* call default window procedure */
8925 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8928 return 0;
8931 /***
8932 * DESCRIPTION:
8933 * Registers the window class.
8935 * PARAMETER(S):
8936 * None
8938 * RETURN:
8939 * None
8941 void LISTVIEW_Register(void)
8943 WNDCLASSW wndClass;
8945 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8946 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8947 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8948 wndClass.cbClsExtra = 0;
8949 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8950 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8951 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8952 wndClass.lpszClassName = WC_LISTVIEWW;
8953 RegisterClassW(&wndClass);
8956 /***
8957 * DESCRIPTION:
8958 * Unregisters the window class.
8960 * PARAMETER(S):
8961 * None
8963 * RETURN:
8964 * None
8966 void LISTVIEW_Unregister(void)
8968 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8971 /***
8972 * DESCRIPTION:
8973 * Handle any WM_COMMAND messages
8975 * PARAMETER(S):
8977 * RETURN:
8979 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8981 switch (HIWORD(wParam))
8983 case EN_UPDATE:
8986 * Adjust the edit window size
8988 WCHAR buffer[1024];
8989 HDC hdc = GetDC(infoPtr->hwndEdit);
8990 HFONT hFont, hOldFont = 0;
8991 RECT rect;
8992 SIZE sz;
8993 int len;
8995 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8996 GetWindowRect(infoPtr->hwndEdit, &rect);
8998 /* Select font to get the right dimension of the string */
8999 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9000 if(hFont != 0)
9002 hOldFont = SelectObject(hdc, hFont);
9005 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9007 TEXTMETRICW textMetric;
9009 /* Add Extra spacing for the next character */
9010 GetTextMetricsW(hdc, &textMetric);
9011 sz.cx += (textMetric.tmMaxCharWidth * 2);
9013 SetWindowPos (
9014 infoPtr->hwndEdit,
9015 HWND_TOP,
9018 sz.cx,
9019 rect.bottom - rect.top,
9020 SWP_DRAWFRAME|SWP_NOMOVE);
9022 if(hFont != 0)
9023 SelectObject(hdc, hOldFont);
9025 ReleaseDC(infoPtr->hwndSelf, hdc);
9027 break;
9030 default:
9031 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
9034 return 0;
9038 /***
9039 * DESCRIPTION:
9040 * Subclassed edit control windproc function
9042 * PARAMETER(S):
9044 * RETURN:
9046 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9047 WPARAM wParam, LPARAM lParam, BOOL isW)
9049 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9050 static BOOL bIgnoreKillFocus = FALSE;
9051 BOOL cancel = FALSE;
9053 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9054 hwnd, uMsg, wParam, lParam, isW);
9056 switch (uMsg)
9058 case WM_GETDLGCODE:
9059 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9061 case WM_KILLFOCUS:
9062 if(bIgnoreKillFocus) return TRUE;
9063 break;
9065 case WM_DESTROY:
9067 WNDPROC editProc = infoPtr->EditWndProc;
9068 infoPtr->EditWndProc = 0;
9069 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9070 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9073 case WM_KEYDOWN:
9074 if (VK_ESCAPE == (INT)wParam)
9076 cancel = TRUE;
9077 break;
9079 else if (VK_RETURN == (INT)wParam)
9080 break;
9082 default:
9083 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9086 if (infoPtr->bEditing)
9088 LPWSTR buffer = NULL;
9090 if (!cancel)
9092 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9094 if (len)
9096 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9098 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9099 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9103 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9104 /* eg. Using a messagebox */
9105 bIgnoreKillFocus = TRUE;
9106 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9108 if (buffer) COMCTL32_Free(buffer);
9110 bIgnoreKillFocus = FALSE;
9113 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9114 return TRUE;
9117 /***
9118 * DESCRIPTION:
9119 * Subclassed edit control windproc function
9121 * PARAMETER(S):
9123 * RETURN:
9125 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9127 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9130 /***
9131 * DESCRIPTION:
9132 * Subclassed edit control windproc function
9134 * PARAMETER(S):
9136 * RETURN:
9138 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9140 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9143 /***
9144 * DESCRIPTION:
9145 * Creates a subclassed edit cotrol
9147 * PARAMETER(S):
9149 * RETURN:
9151 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9152 INT x, INT y, INT width, INT height, BOOL isW)
9154 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9155 HWND hedit;
9156 SIZE sz;
9157 HDC hdc;
9158 HDC hOldFont=0;
9159 TEXTMETRICW textMetric;
9160 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9162 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9164 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9165 hdc = GetDC(infoPtr->hwndSelf);
9167 /* Select the font to get appropriate metric dimensions */
9168 if(infoPtr->hFont != 0)
9169 hOldFont = SelectObject(hdc, infoPtr->hFont);
9171 /*Get String Lenght in pixels */
9172 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9174 /*Add Extra spacing for the next character */
9175 GetTextMetricsW(hdc, &textMetric);
9176 sz.cx += (textMetric.tmMaxCharWidth * 2);
9178 if(infoPtr->hFont != 0)
9179 SelectObject(hdc, hOldFont);
9181 ReleaseDC(infoPtr->hwndSelf, hdc);
9182 if (isW)
9183 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9184 else
9185 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9187 if (!hedit) return 0;
9189 infoPtr->EditWndProc = (WNDPROC)
9190 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9191 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9193 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9195 return hedit;