Rewrite the begin/end label edit procedures.
[wine/multimedia.git] / dlls / comctl32 / listview.c
blob8ca238972f9dabfd14dd85dafd50dd34d33166d7
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, 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, 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, BOOL internal)
742 return LISTVIEW_GetItemT(infoPtr, lpLVItem, internal, 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, TRUE)) 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", 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;
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, TRUE)) 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;
1383 RECT FullText;
1385 Label.left = TopLeft.x + Origin.x;
1386 Label.top = TopLeft.y + Origin.y + ICON_TOP_PADDING_HITABLE +
1387 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1389 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1390 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
1392 Label.left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
1393 Label.right = Label.left + nLabelWidth;
1394 Label.bottom = Label.top + infoPtr->ntmHeight + 1;
1395 Label.bottom += HEIGHT_PADDING;
1397 else
1399 Label.right = Label.left + infoPtr->nItemWidth;
1400 Label.bottom = Label.top + infoPtr->nItemHeight + HEIGHT_PADDING;
1401 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, &Label);
1403 FullText = Label;
1404 InflateRect(&FullText, 2, 0);
1405 if (lprcLabel) *lprcLabel = Label;
1406 if (lprcText) *lprcText = FullText;
1407 TRACE("hwnd=%x, item=%d, label=(%d,%d)-(%d,%d), fulltext=(%d,%d)-(%d,%d)\n",
1408 infoPtr->hwndSelf, nItem,
1409 Label.left, Label.top, Label.right, Label.bottom,
1410 FullText.left, FullText.top, FullText.right, FullText.bottom);
1412 else return FALSE;
1414 else if (uView == LVS_SMALLICON)
1416 INT nLeftPos, nLabelWidth;
1418 nLeftPos = Label.left = Position.x + Origin.x;
1419 Label.top = Position.y + Origin.y;
1420 Label.bottom = Label.top + infoPtr->nItemHeight;
1422 if (infoPtr->himlState != NULL)
1423 Label.left += infoPtr->iconStateSize.cx;
1425 if (infoPtr->himlSmall != NULL)
1426 Label.left += infoPtr->iconSize.cx;
1428 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1429 nLabelWidth += TRAILING_PADDING;
1430 if (Label.left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
1431 Label.right = Label.left + nLabelWidth;
1432 else
1433 Label.right = nLeftPos + infoPtr->nItemWidth;
1434 if (lprcLabel) *lprcLabel = Label;
1435 if (lprcText) *lprcText = Label;
1436 TRACE("hwnd=%x, item=%d, label=(%d,%d)-(%d,%d)\n",
1437 infoPtr->hwndSelf, nItem,
1438 Label.left, Label.top, Label.right, Label.bottom);
1440 else /* LVS_LIST or LVS_REPORT */
1442 INT nLabelWidth;
1444 if (uView != LVS_REPORT)
1446 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1447 nLabelWidth += TRAILING_PADDING;
1448 if (infoPtr->himlSmall) nLabelWidth += IMAGE_PADDING;
1450 else
1451 nLabelWidth = LISTVIEW_GetColumnWidth(infoPtr, 0) - Icon.left;
1453 Label.left = Icon.right;
1454 Label.top = Position.y;
1455 Label.right = Label.left + nLabelWidth;
1456 Label.bottom = Label.top + infoPtr->nItemHeight;
1458 if (Label.right - Position.x > infoPtr->nItemWidth)
1459 Label.right = Position.x + infoPtr->nItemWidth;
1460 if (lprcLabel) *lprcLabel = Label;
1461 if (lprcText) *lprcText = Label;
1464 TRACE("hwnd=%x, item=%d, label=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Label));
1466 /***********************************************************/
1467 /* compute boundary box for the item (ala LVM_GETITEMRECT) */
1468 /***********************************************************/
1470 if (uView == LVS_REPORT)
1472 Boundary.left = TopLeft.x;
1473 Boundary.top = TopLeft.y;
1474 Boundary.right = Boundary.left + infoPtr->nItemWidth;
1475 Boundary.bottom = Boundary.top + infoPtr->nItemHeight;
1476 if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
1477 Boundary.left += nIndent;
1478 Boundary.right -= REPORT_MARGINX;
1479 if (Boundary.right < Boundary.left) Boundary.right = Boundary.left;
1481 else
1483 UnionRect(&Boundary, &Icon, &Label);
1486 TRACE("hwnd=%x, item=%d, boundary=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Boundary));
1487 if (lprcBoundary) *lprcBoundary = Boundary;
1489 return TRUE;
1492 /***
1493 * DESCRIPTION:
1494 * Aligns the items with the top edge of the window.
1496 * PARAMETER(S):
1497 * [I] infoPtr : valid pointer to the listview structure
1499 * RETURN:
1500 * None
1502 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1504 UINT uView = LISTVIEW_GetType(infoPtr);
1505 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1506 POINT ptItem;
1507 RECT rcView;
1508 INT i, off_x=0, off_y=0;
1510 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1512 /* Since SetItemPosition uses upper-left of icon, and for
1513 style=LVS_ICON the icon is not left adjusted, get the offset */
1514 if (uView == LVS_ICON)
1516 off_y = ICON_TOP_PADDING;
1517 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1519 ptItem.x = off_x;
1520 ptItem.y = off_y;
1521 ZeroMemory(&rcView, sizeof(RECT));
1522 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1523 off_x, off_y,
1524 infoPtr->rcList.left, infoPtr->rcList.right);
1526 if (nListWidth > infoPtr->nItemWidth)
1528 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1530 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1532 ptItem.x = off_x;
1533 ptItem.y += infoPtr->nItemHeight;
1536 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1537 ptItem.x += infoPtr->nItemWidth;
1538 rcView.right = max(rcView.right, ptItem.x);
1541 rcView.right -= off_x;
1542 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1544 else
1546 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1548 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1549 ptItem.y += infoPtr->nItemHeight;
1552 rcView.right = infoPtr->nItemWidth;
1553 rcView.bottom = ptItem.y-off_y;
1556 infoPtr->rcView = rcView;
1560 /***
1561 * DESCRIPTION:
1562 * Aligns the items with the left edge of the window.
1564 * PARAMETER(S):
1565 * [I] infoPtr : valid pointer to the listview structure
1567 * RETURN:
1568 * None
1570 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1572 UINT uView = LISTVIEW_GetType(infoPtr);
1573 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1574 POINT ptItem;
1575 RECT rcView;
1576 INT i, off_x=0, off_y=0;
1578 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1580 /* Since SetItemPosition uses upper-left of icon, and for
1581 style=LVS_ICON the icon is not left adjusted, get the offset */
1582 if (uView == LVS_ICON)
1584 off_y = ICON_TOP_PADDING;
1585 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1587 ptItem.x = off_x;
1588 ptItem.y = off_y;
1589 ZeroMemory(&rcView, sizeof(RECT));
1590 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1592 if (nListHeight > infoPtr->nItemHeight)
1594 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1596 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1598 ptItem.y = off_y;
1599 ptItem.x += infoPtr->nItemWidth;
1602 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1603 ptItem.y += infoPtr->nItemHeight;
1604 rcView.bottom = max(rcView.bottom, ptItem.y);
1607 rcView.right = ptItem.x + infoPtr->nItemWidth;
1609 else
1611 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1613 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1614 ptItem.x += infoPtr->nItemWidth;
1617 rcView.bottom = infoPtr->nItemHeight;
1618 rcView.right = ptItem.x;
1621 infoPtr->rcView = rcView;
1626 /***
1627 * DESCRIPTION:
1628 * Retrieves the bounding rectangle of all the items.
1630 * PARAMETER(S):
1631 * [I] infoPtr : valid pointer to the listview structure
1632 * [O] lprcView : bounding rectangle
1634 * RETURN:
1635 * SUCCESS : TRUE
1636 * FAILURE : FALSE
1638 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1640 POINT ptOrigin;
1642 TRACE("(lprcView=%p)\n", lprcView);
1644 if (!lprcView) return FALSE;
1646 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1648 *lprcView = infoPtr->rcView;
1649 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1651 TRACE("lprcView=%s\n", debugrect(lprcView));
1653 return TRUE;
1656 /***
1657 * DESCRIPTION:
1658 * Retrieves the subitem pointer associated with the subitem index.
1660 * PARAMETER(S):
1661 * [I] HDPA : DPA handle for a specific item
1662 * [I] INT : index of subitem
1664 * RETURN:
1665 * SUCCESS : subitem pointer
1666 * FAILURE : NULL
1668 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1669 INT nSubItem)
1671 LISTVIEW_SUBITEM *lpSubItem;
1672 INT i;
1674 /* we should binary search here if need be */
1675 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1677 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1678 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1679 return lpSubItem;
1682 return NULL;
1686 /***
1687 * DESCRIPTION:
1688 * Calculates the width of a specific item.
1690 * PARAMETER(S):
1691 * [I] infoPtr : valid pointer to the listview structure
1692 * [I] nItem : item to calculate width, or -1 for max of all
1694 * RETURN:
1695 * Returns the width of an item width an item.
1697 static INT LISTVIEW_CalculateWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1699 UINT uView = LISTVIEW_GetType(infoPtr);
1700 INT nItemWidth = 0, i;
1702 if (uView == LVS_ICON)
1703 nItemWidth = infoPtr->iconSpacing.cx;
1704 else if (uView == LVS_REPORT)
1706 INT nHeaderItemCount;
1707 RECT rcHeaderItem;
1709 /* calculate width of header */
1710 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1711 for (i = 0; i < nHeaderItemCount; i++)
1712 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
1713 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1715 else
1717 INT nLabelWidth;
1719 if (GETITEMCOUNT(infoPtr) == 0) return DEFAULT_COLUMN_WIDTH;
1721 /* get width of string */
1722 if (nItem == -1)
1724 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1726 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1727 nItemWidth = max(nItemWidth, nLabelWidth);
1730 else
1731 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1732 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
1733 nItemWidth += WIDTH_PADDING;
1734 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
1735 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
1736 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1739 return max(nItemWidth, 1);
1742 /***
1743 * DESCRIPTION:
1744 * Calculates the max width of any item in the list.
1746 * PARAMETER(S):
1747 * [I] infoPtr : valid pointer to the listview structure
1748 * [I] LONG : window style
1750 * RETURN:
1751 * Returns item width.
1753 static inline INT LISTVIEW_GetItemWidth(LISTVIEW_INFO *infoPtr)
1755 return LISTVIEW_CalculateWidth(infoPtr, -1);
1758 /***
1759 * DESCRIPTION:
1760 * Retrieves and saves important text metrics info for the current
1761 * Listview font.
1763 * PARAMETER(S):
1764 * [I] infoPtr : valid pointer to the listview structure
1767 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1769 TEXTMETRICW tm;
1770 HDC hdc = GetDC(infoPtr->hwndSelf);
1771 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1772 INT oldHeight, oldACW;
1774 GetTextMetricsW(hdc, &tm);
1776 oldHeight = infoPtr->ntmHeight;
1777 oldACW = infoPtr->ntmAveCharWidth;
1778 infoPtr->ntmHeight = tm.tmHeight;
1779 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1781 SelectObject(hdc, hOldFont);
1782 ReleaseDC(infoPtr->hwndSelf, hdc);
1783 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1784 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1788 /***
1789 * DESCRIPTION:
1790 * Calculates the height of an item.
1792 * PARAMETER(S):
1793 * [I] infoPtr : valid pointer to the listview structure
1795 * RETURN:
1796 * Returns item height.
1798 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1800 INT nItemHeight;
1802 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
1803 nItemHeight = infoPtr->iconSpacing.cy;
1804 else
1806 nItemHeight = infoPtr->ntmHeight;
1807 if (infoPtr->himlState)
1808 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
1809 if (infoPtr->himlSmall)
1810 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
1811 if (infoPtr->himlState || infoPtr->himlSmall)
1812 nItemHeight += HEIGHT_PADDING;
1814 return nItemHeight;
1817 #if 0
1818 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1820 RANGE *selection;
1821 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1822 INT i;
1824 TRACE("Selections are:\n");
1825 for (i = 0; i < topSelection; i++)
1827 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1828 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1831 #endif
1833 /***
1834 * DESCRIPTION:
1835 * A compare function for selection ranges
1837 *PARAMETER(S)
1838 * [I] range1 : pointer to selection range 1;
1839 * [I] range2 : pointer to selection range 2;
1840 * [I] flags : flags
1842 *RETURNS:
1843 * >0 : if Item 1 > Item 2
1844 * <0 : if Item 2 > Item 1
1845 * 0 : if Item 1 == Item 2
1847 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2, LPARAM flags)
1849 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
1850 return -1;
1851 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
1852 return 1;
1853 return 0;
1856 /***
1857 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
1859 static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1861 RANGE selection;
1862 LVITEMW lvItem;
1863 INT index, i;
1865 TRACE("range (%i - %i)\n", lower, upper);
1867 /* try find overlapping selections first */
1868 selection.lower = lower - 1;
1869 selection.upper = upper + 1;
1870 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1871 LISTVIEW_CompareSelectionRanges, 0, 0);
1873 if (index == -1)
1875 RANGE *newsel;
1877 /* create the brand new selection to insert */
1878 newsel = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
1879 if(!newsel) return FALSE;
1880 newsel->lower = lower;
1881 newsel->upper = upper;
1883 /* figure out where to insert it */
1884 index = DPA_Search(infoPtr->hdpaSelectionRanges, newsel, 0,
1885 LISTVIEW_CompareSelectionRanges, 0, DPAS_INSERTAFTER);
1886 if (index == -1) index = 0;
1888 /* and get it over with */
1889 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
1891 else
1893 RANGE *chksel, *mrgsel;
1894 INT fromindex, mergeindex;
1896 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1897 if (!chksel) return FALSE;
1898 TRACE("Merge with index %i (%d - %d)\n",
1899 index, chksel->lower, chksel->upper);
1901 chksel->lower = min(lower, chksel->lower);
1902 chksel->upper = max(upper, chksel->upper);
1904 TRACE("New range %i (%d - %d)\n",
1905 index, chksel->lower, chksel->upper);
1907 /* merge now common selection ranges */
1908 fromindex = 0;
1909 selection.lower = chksel->lower - 1;
1910 selection.upper = chksel->upper + 1;
1914 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, fromindex,
1915 LISTVIEW_CompareSelectionRanges, 0, 0);
1916 if (mergeindex == -1) break;
1917 if (mergeindex == index)
1919 fromindex = index + 1;
1920 continue;
1923 TRACE("Merge with index %i\n", mergeindex);
1925 mrgsel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, mergeindex);
1926 if (!mrgsel) return FALSE;
1928 chksel->lower = min(chksel->lower, mrgsel->lower);
1929 chksel->upper = max(chksel->upper, mrgsel->upper);
1930 COMCTL32_Free(mrgsel);
1931 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, mergeindex);
1932 if (mergeindex < index) index --;
1933 } while(1);
1936 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
1938 if (adj_sel_only) return TRUE;
1940 /* set the selection on items */
1941 lvItem.state = LVIS_SELECTED;
1942 lvItem.stateMask = LVIS_SELECTED;
1943 for(i = lower; i <= upper; i++)
1944 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
1946 return TRUE;
1949 /***
1950 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
1952 static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1954 RANGE remsel, tmpsel, *chksel;
1955 BOOL done = FALSE;
1956 LVITEMW lvItem;
1957 INT index, i;
1959 lvItem.state = 0;
1960 lvItem.stateMask = LVIS_SELECTED;
1962 remsel.lower = lower;
1963 remsel.upper = upper;
1965 TRACE("range: (%d - %d)\n", remsel.lower, remsel.upper);
1969 index = DPA_Search(infoPtr->hdpaSelectionRanges, &remsel, 0,
1970 LISTVIEW_CompareSelectionRanges, 0, 0);
1971 if (index == -1) return TRUE;
1973 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1974 if (!chksel) return FALSE;
1976 TRACE("Matches range index %i (%d - %d)\n",
1977 index, chksel->lower, chksel->upper);
1979 /* case 1: Same range */
1980 if ( (chksel->upper == remsel.upper) &&
1981 (chksel->lower == remsel.lower) )
1983 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1984 done = TRUE;
1986 /* case 2: engulf */
1987 else if ( (chksel->upper <= remsel.upper) &&
1988 (chksel->lower >= remsel.lower) )
1990 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1992 /* case 3: overlap upper */
1993 else if ( (chksel->upper < remsel.upper) &&
1994 (chksel->lower < remsel.lower) )
1996 chksel->upper = remsel.lower - 1;
1998 /* case 4: overlap lower */
1999 else if ( (chksel->upper > remsel.upper) &&
2000 (chksel->lower > remsel.lower) )
2002 chksel->lower = remsel.upper + 1;
2004 /* case 5: fully internal */
2005 else
2007 RANGE *newsel =
2008 (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2009 if (!newsel) return FALSE;
2010 tmpsel = *chksel;
2011 newsel->lower = chksel->lower;
2012 newsel->upper = remsel.lower - 1;
2013 chksel->lower = remsel.upper + 1;
2014 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
2015 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
2016 chksel = &tmpsel;
2019 if (adj_sel_only) continue;
2021 /* here, chksel holds the selection to delete */
2022 for (i = chksel->lower; i <= chksel->upper; i++)
2023 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2025 while(!done);
2027 return TRUE;
2031 * DESCRIPTION:
2032 * Adds a selection range.
2034 * PARAMETER(S):
2035 * [I] infoPtr : valid pointer to the listview structure
2036 * [I] lower : lower item index
2037 * [I] upper : upper item index
2039 * RETURN:
2040 * Success: TRUE
2041 * Failure: FALSE
2043 static inline BOOL LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2045 return add_selection_range(infoPtr, lower, upper, FALSE);
2048 /***
2049 * DESCRIPTION:
2050 * Removes a range selections.
2052 * PARAMETER(S):
2053 * [I] infoPtr : valid pointer to the listview structure
2054 * [I] lower : lower item index
2055 * [I] upper : upper item index
2057 * RETURN:
2058 * Success: TRUE
2059 * Failure: FALSE
2061 static inline BOOL LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2063 return remove_selection_range(infoPtr, lower, upper, FALSE);
2066 /***
2067 * DESCRIPTION:
2068 * Removes all selection ranges
2070 * Parameters(s):
2071 * [I] infoPtr : valid pointer to the listview structure
2073 * RETURNS:
2074 * SUCCESS : TRUE
2075 * FAILURE : TRUE
2077 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
2079 RANGE *sel;
2080 static BOOL removing_all_selections = FALSE;
2082 if (removing_all_selections) return TRUE;
2084 removing_all_selections = TRUE;
2086 TRACE("()\n");
2090 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, 0);
2091 if (sel) LISTVIEW_RemoveSelectionRange(infoPtr, sel->lower, sel->upper);
2093 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
2095 removing_all_selections = FALSE;
2097 return TRUE;
2100 /***
2101 * DESCRIPTION:
2102 * Manages the item focus.
2104 * PARAMETER(S):
2105 * [I] infoPtr : valid pointer to the listview structure
2106 * [I] INT : item index
2108 * RETURN:
2109 * TRUE : focused item changed
2110 * FALSE : focused item has NOT changed
2112 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2114 INT oldFocus = infoPtr->nFocusedItem;
2115 LVITEMW lvItem;
2117 lvItem.state = LVIS_FOCUSED;
2118 lvItem.stateMask = LVIS_FOCUSED;
2119 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2121 return oldFocus != infoPtr->nFocusedItem;
2125 * DESCRIPTION:
2126 * Updates the various indices after an item has been inserted or deleted.
2128 * PARAMETER(S):
2129 * [I] infoPtr : valid pointer to the listview structure
2130 * [I] nItem : item index
2131 * [I] direction : Direction of shift, +1 or -1.
2133 * RETURN:
2134 * None
2136 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2138 RANGE selection,*checkselection;
2139 INT index;
2141 TRACE("Shifting %iu, %i steps\n",nItem,direction);
2143 selection.upper = nItem;
2144 selection.lower = nItem;
2146 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
2147 LISTVIEW_CompareSelectionRanges,
2148 0,DPAS_SORTED|DPAS_INSERTAFTER);
2150 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
2152 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
2153 if ((checkselection->lower >= nItem)&&
2154 ((int)(checkselection->lower + direction) >= 0))
2155 checkselection->lower += direction;
2156 if ((checkselection->upper >= nItem)&&
2157 ((int)(checkselection->upper + direction) >= 0))
2158 checkselection->upper += direction;
2159 index ++;
2162 /* Note that the following will fail if direction != +1 and -1 */
2163 if (infoPtr->nSelectionMark > nItem)
2164 infoPtr->nSelectionMark += direction;
2165 else if (infoPtr->nSelectionMark == nItem)
2167 if (direction > 0)
2168 infoPtr->nSelectionMark += direction;
2169 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
2170 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
2173 if (infoPtr->nFocusedItem > nItem)
2174 infoPtr->nFocusedItem += direction;
2175 else if (infoPtr->nFocusedItem == nItem)
2177 if (direction > 0)
2178 infoPtr->nFocusedItem += direction;
2179 else
2181 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
2182 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
2183 if (infoPtr->nFocusedItem >= 0)
2184 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
2187 /* But we are not supposed to modify nHotItem! */
2192 * DESCRIPTION:
2193 * Adds a block of selections.
2195 * PARAMETER(S):
2196 * [I] infoPtr : valid pointer to the listview structure
2197 * [I] INT : item index
2199 * RETURN:
2200 * None
2202 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2204 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2205 INT nLast = max(infoPtr->nSelectionMark, nItem);
2206 INT i;
2207 LVITEMW item;
2209 if (nFirst == -1)
2210 nFirst = nItem;
2212 item.state = LVIS_SELECTED;
2213 item.stateMask = LVIS_SELECTED;
2215 /* FIXME: this is not correct LVS_OWNERDATA
2216 * See docu for LVN_ITEMCHANGED. Is there something similar for
2217 * RemoveGroupSelection (is there such a thing?)?
2219 for (i = nFirst; i <= nLast; i++)
2220 LISTVIEW_SetItemState(infoPtr,i,&item);
2222 LISTVIEW_SetItemFocus(infoPtr, nItem);
2223 infoPtr->nSelectionMark = nItem;
2227 /***
2228 * DESCRIPTION:
2229 * Sets a single group selection.
2231 * PARAMETER(S):
2232 * [I] infoPtr : valid pointer to the listview structure
2233 * [I] INT : item index
2235 * RETURN:
2236 * None
2238 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2240 UINT uView = LISTVIEW_GetType(infoPtr);
2241 INT i, nFirst, nLast;
2242 LVITEMW item;
2243 POINT ptItem;
2244 RECT rcSel;
2246 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2248 if (infoPtr->nSelectionMark == -1)
2249 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2250 else
2252 nFirst = min(infoPtr->nSelectionMark, nItem);
2253 nLast = max(infoPtr->nSelectionMark, nItem);
2256 else
2258 RECT rcItem, rcSelMark;
2260 rcItem.left = LVIR_BOUNDS;
2261 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2262 rcSelMark.left = LVIR_BOUNDS;
2263 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2264 UnionRect(&rcSel, &rcItem, &rcSelMark);
2265 nFirst = nLast = -1;
2268 item.stateMask = LVIS_SELECTED;
2270 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2272 if (nFirst > -1)
2273 item.state = (i < nFirst) || (i > nLast) ? 0 : LVIS_SELECTED;
2274 else
2276 LISTVIEW_GetItemPosition(infoPtr, i, &ptItem);
2277 item.state = PtInRect(&rcSel, ptItem) ? LVIS_SELECTED : 0;
2279 LISTVIEW_SetItemState(infoPtr, i, &item);
2281 LISTVIEW_SetItemFocus(infoPtr, nItem);
2284 /***
2285 * DESCRIPTION:
2286 * Sets a single selection.
2288 * PARAMETER(S):
2289 * [I] infoPtr : valid pointer to the listview structure
2290 * [I] INT : item index
2292 * RETURN:
2293 * None
2295 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2297 LVITEMW lvItem;
2299 LISTVIEW_RemoveAllSelections(infoPtr);
2301 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2302 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2303 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2305 infoPtr->nSelectionMark = nItem;
2308 /***
2309 * DESCRIPTION:
2310 * Set selection(s) with keyboard.
2312 * PARAMETER(S):
2313 * [I] infoPtr : valid pointer to the listview structure
2314 * [I] INT : item index
2316 * RETURN:
2317 * SUCCESS : TRUE (needs to be repainted)
2318 * FAILURE : FALSE (nothing has changed)
2320 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2322 /* FIXME: pass in the state */
2323 LONG lStyle = infoPtr->dwStyle;
2324 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2325 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2326 BOOL bResult = FALSE;
2328 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2330 if (lStyle & LVS_SINGLESEL)
2332 bResult = TRUE;
2333 LISTVIEW_SetSelection(infoPtr, nItem);
2334 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2336 else
2338 if (wShift)
2340 bResult = TRUE;
2341 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2343 else if (wCtrl)
2345 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2347 else
2349 bResult = TRUE;
2350 LISTVIEW_SetSelection(infoPtr, nItem);
2351 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2356 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2357 return bResult;
2360 /***
2361 * DESCRIPTION:
2362 * Selects an item based on coordinates.
2364 * PARAMETER(S):
2365 * [I] infoPtr : valid pointer to the listview structure
2366 * [I] pt : mouse click ccordinates
2368 * RETURN:
2369 * SUCCESS : item index
2370 * FAILURE : -1
2372 static INT LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, POINT pt)
2374 RANGE visrange;
2375 RECT rcItem;
2376 INT i;
2378 visrange = LISTVIEW_GetVisibleRange(infoPtr);
2379 for (i = visrange.lower; i <= visrange.upper; i++)
2381 rcItem.left = LVIR_SELECTBOUNDS;
2382 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
2384 TRACE("i=%d, rcItem=%s\n", i, debugrect(&rcItem));
2385 if (PtInRect(&rcItem, pt)) return i;
2388 return -1;
2391 /***
2392 * DESCRIPTION:
2393 * Called when the mouse is being actively tracked and has hovered for a specified
2394 * amount of time
2396 * PARAMETER(S):
2397 * [I] infoPtr : valid pointer to the listview structure
2398 * [I] fwKeys : key indicator
2399 * [I] pts : mouse position
2401 * RETURN:
2402 * 0 if the message was processed, non-zero if there was an error
2404 * INFO:
2405 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2406 * over the item for a certain period of time.
2409 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2411 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2412 /* FIXME: select the item!!! */
2413 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2415 return 0;
2418 /***
2419 * DESCRIPTION:
2420 * Called whenever WM_MOUSEMOVE is received.
2422 * PARAMETER(S):
2423 * [I] infoPtr : valid pointer to the listview structure
2424 * [I] fwKeys : key indicator
2425 * [I] pts : mouse position
2427 * RETURN:
2428 * 0 if the message is processed, non-zero if there was an error
2430 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2432 TRACKMOUSEEVENT trackinfo;
2434 /* see if we are supposed to be tracking mouse hovering */
2435 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2436 /* fill in the trackinfo struct */
2437 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2438 trackinfo.dwFlags = TME_QUERY;
2439 trackinfo.hwndTrack = infoPtr->hwndSelf;
2440 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2442 /* see if we are already tracking this hwnd */
2443 _TrackMouseEvent(&trackinfo);
2445 if(!(trackinfo.dwFlags & TME_HOVER)) {
2446 trackinfo.dwFlags = TME_HOVER;
2448 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2449 _TrackMouseEvent(&trackinfo);
2453 return 0;
2457 /***
2458 * Tests wheather the item is assignable to a list with style lStyle
2460 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2462 if ( (lpLVItem->mask & LVIF_TEXT) &&
2463 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2464 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2466 return TRUE;
2469 /***
2470 * DESCRIPTION:
2471 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2473 * PARAMETER(S):
2474 * [I] infoPtr : valid pointer to the listview structure
2475 * [I] lpLVItem : valid pointer to new item atttributes
2476 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2478 * RETURN:
2479 * SUCCESS : TRUE
2480 * FAILURE : FALSE
2482 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2484 LONG lStyle = infoPtr->dwStyle;
2485 NMLISTVIEW nmlv;
2486 INT oldState;
2488 /* a virtual livst view stores only state for the main item */
2489 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2491 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2493 /* we're done if we don't need to change anything we handle */
2494 if ( (oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2495 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED)) return FALSE;
2498 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2499 * by LVS_OWERNDATA list controls
2502 /* if we handle the focus, and we're asked to change it, do it now */
2503 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2505 if (lpLVItem->state & LVIS_FOCUSED)
2506 infoPtr->nFocusedItem = lpLVItem->iItem;
2507 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2508 infoPtr->nFocusedItem = -1;
2511 /* and the selection is the only other state a virtual list may hold */
2512 if (lpLVItem->stateMask & LVIS_SELECTED)
2514 if (lpLVItem->state & LVIS_SELECTED)
2516 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2517 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2519 else
2520 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2523 /* notify the parent now that things have changed */
2524 ZeroMemory(&nmlv, sizeof(nmlv));
2525 nmlv.iItem = lpLVItem->iItem;
2526 nmlv.uNewState = lpLVItem->state;
2527 nmlv.uOldState = oldState;
2528 nmlv.uChanged = LVIF_STATE;
2529 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2531 return TRUE;
2534 /***
2535 * DESCRIPTION:
2536 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2538 * PARAMETER(S):
2539 * [I] infoPtr : valid pointer to the listview structure
2540 * [I] lpLVItem : valid pointer to new item atttributes
2541 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2543 * RETURN:
2544 * SUCCESS : TRUE
2545 * FAILURE : FALSE
2547 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2549 LONG lStyle = infoPtr->dwStyle;
2550 UINT uView = lStyle & LVS_TYPEMASK;
2551 HDPA hdpaSubItems;
2552 LISTVIEW_ITEM *lpItem;
2553 NMLISTVIEW nmlv;
2554 UINT uChanged = 0;
2556 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2557 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2559 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2560 if (!lpItem) return FALSE;
2562 /* determine what fields will change */
2563 if ((lpLVItem->mask & LVIF_STATE) &&
2564 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask))
2565 uChanged |= LVIF_STATE;
2567 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2568 uChanged |= LVIF_IMAGE;
2570 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2571 uChanged |= LVIF_PARAM;
2573 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2574 uChanged |= LVIF_INDENT;
2576 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2577 uChanged |= LVIF_TEXT;
2579 if (!uChanged) return TRUE;
2581 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2582 nmlv.iItem = lpLVItem->iItem;
2583 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2584 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2585 nmlv.uChanged = uChanged;
2586 nmlv.lParam = lpItem->lParam;
2588 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2589 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2590 return FALSE;
2592 /* copy information */
2593 if (lpLVItem->mask & LVIF_TEXT)
2594 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2596 if (lpLVItem->mask & LVIF_IMAGE)
2597 lpItem->hdr.iImage = lpLVItem->iImage;
2599 if (lpLVItem->mask & LVIF_PARAM)
2600 lpItem->lParam = lpLVItem->lParam;
2602 if (lpLVItem->mask & LVIF_INDENT)
2603 lpItem->iIndent = lpLVItem->iIndent;
2605 if (uChanged & LVIF_STATE)
2607 lpItem->state &= ~lpLVItem->stateMask;
2608 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2609 if (nmlv.uNewState & LVIS_SELECTED)
2611 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2612 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2614 else if (lpLVItem->stateMask & LVIS_SELECTED)
2615 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2617 /* if we are asked to change focus, and we manage it, do it */
2618 if (nmlv.uNewState & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2620 if (lpLVItem->state & LVIS_FOCUSED)
2622 infoPtr->nFocusedItem = lpLVItem->iItem;
2623 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2625 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2626 infoPtr->nFocusedItem = -1;
2630 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2631 if((uChanged & LVIF_TEXT) && ((uView == LVS_LIST) || (uView == LVS_SMALLICON)))
2633 int item_width = LISTVIEW_CalculateWidth(infoPtr, lpLVItem->iItem);
2634 if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width;
2637 /* if we're inserting the item, we're done */
2638 if (!lpItem->valid) return TRUE;
2640 /* send LVN_ITEMCHANGED notification */
2641 nmlv.lParam = lpItem->lParam;
2642 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2644 return TRUE;
2647 /***
2648 * DESCRIPTION:
2649 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2651 * PARAMETER(S):
2652 * [I] infoPtr : valid pointer to the listview structure
2653 * [I] lpLVItem : valid pointer to new subitem atttributes
2654 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2656 * RETURN:
2657 * SUCCESS : TRUE
2658 * FAILURE : FALSE
2660 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2662 HDPA hdpaSubItems;
2663 LISTVIEW_SUBITEM *lpSubItem;
2665 /* set subitem only if column is present */
2666 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2667 return FALSE;
2669 /* First do some sanity checks */
2670 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2672 /* get the subitem structure, and create it if not there */
2673 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2674 if (!hdpaSubItems) return FALSE;
2676 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2677 if (!lpSubItem)
2679 LISTVIEW_SUBITEM *tmpSubItem;
2680 INT i;
2682 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2683 if (!lpSubItem) return FALSE;
2684 /* we could binary search here, if need be...*/
2685 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2687 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2688 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2690 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2692 COMCTL32_Free(lpSubItem);
2693 return FALSE;
2695 lpSubItem->iSubItem = lpLVItem->iSubItem;
2698 if (lpLVItem->mask & LVIF_IMAGE)
2699 lpSubItem->hdr.iImage = lpLVItem->iImage;
2701 if (lpLVItem->mask & LVIF_TEXT)
2702 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2704 return TRUE;
2707 /***
2708 * DESCRIPTION:
2709 * Sets item attributes.
2711 * PARAMETER(S):
2712 * [I] infoPtr : valid pointer to the listview structure
2713 * [I] LPLVITEM : new item atttributes
2714 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2716 * RETURN:
2717 * SUCCESS : TRUE
2718 * FAILURE : FALSE
2720 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2722 INT oldFocus = infoPtr->nFocusedItem;
2723 LPWSTR pszText = NULL;
2724 BOOL bResult;
2726 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2728 if (!lpLVItem || lpLVItem->iItem < 0 ||
2729 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2730 return FALSE;
2732 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2733 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
2735 pszText = lpLVItem->pszText;
2736 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
2739 /* actually set the fields */
2740 if (infoPtr->dwStyle & LVS_OWNERDATA)
2741 bResult = set_owner_item(infoPtr, lpLVItem, TRUE);
2742 else
2744 /* sanity checks first */
2745 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
2747 if (lpLVItem->iSubItem)
2748 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
2749 else
2750 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
2753 /* redraw item, if necessary */
2754 if (bResult && !infoPtr->bIsDrawing)
2756 if (oldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
2758 LISTVIEW_ToggleFocusRect(infoPtr);
2759 /* Note that ->rcLargeFocus is normally all zero, so
2760 * no second InvalidateRect is issued.
2762 * However, when a large icon style is drawn (LVS_ICON),
2763 * the rectangle drawn is saved in rcLastDraw. That way
2764 * the InvalidateRect will invalidate the entire area drawn
2766 if (!IsRectEmpty(&infoPtr->rcLargeFocus))
2767 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcLargeFocus);
2769 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
2771 /* restore text */
2772 if (pszText)
2774 textfreeT(lpLVItem->pszText, isW);
2775 lpLVItem->pszText = pszText;
2778 return bResult;
2781 /***
2782 * DESCRIPTION:
2783 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2785 * PARAMETER(S):
2786 * [I] infoPtr : valid pointer to the listview structure
2788 * RETURN:
2789 * item index
2791 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2793 LONG lStyle = infoPtr->dwStyle;
2794 UINT uView = lStyle & LVS_TYPEMASK;
2795 INT nItem = 0;
2796 SCROLLINFO scrollInfo;
2798 scrollInfo.cbSize = sizeof(SCROLLINFO);
2799 scrollInfo.fMask = SIF_POS;
2801 if (uView == LVS_LIST)
2803 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2804 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2806 else if (uView == LVS_REPORT)
2808 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2809 nItem = scrollInfo.nPos;
2811 else
2813 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2814 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
2817 TRACE("nItem=%d\n", nItem);
2819 return nItem;
2822 /* used by the drawing code */
2823 typedef struct tagTEXTATTR
2825 int bkMode;
2826 COLORREF bkColor;
2827 COLORREF fgColor;
2828 } TEXTATTR;
2830 /* helper function for the drawing code */
2831 static inline void set_text_attr(HDC hdc, TEXTATTR *ta)
2833 ta->bkMode = SetBkMode(hdc, ta->bkMode);
2834 ta->bkColor = SetBkColor(hdc, ta->bkColor);
2835 ta->fgColor = SetTextColor(hdc, ta->fgColor);
2838 /* helper function for the drawing code */
2839 static void select_text_attr(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL isSelected, TEXTATTR *ta)
2841 ta->bkMode = OPAQUE;
2843 if (isSelected && infoPtr->bFocus)
2845 ta->bkColor = comctl32_color.clrHighlight;
2846 ta->fgColor = comctl32_color.clrHighlightText;
2848 else if (isSelected && (infoPtr->dwStyle & LVS_SHOWSELALWAYS))
2850 ta->bkColor = comctl32_color.clr3dFace;
2851 ta->fgColor = comctl32_color.clrBtnText;
2853 else if ( (infoPtr->clrTextBk != CLR_DEFAULT) && (infoPtr->clrTextBk != CLR_NONE) )
2855 ta->bkColor = infoPtr->clrTextBk;
2856 ta->fgColor = infoPtr->clrText;
2858 else
2860 ta->bkMode = TRANSPARENT;
2861 ta->bkColor = GetBkColor(hdc);
2862 ta->fgColor = infoPtr->clrText;
2865 set_text_attr(hdc, ta);
2868 /***
2869 * DESCRIPTION:
2870 * Erases the background of the given rectangle
2872 * PARAMETER(S):
2873 * [I] infoPtr : valid pointer to the listview structure
2874 * [I] hdc : device context handle
2875 * [I] lprcBox : clipping rectangle
2877 * RETURN:
2878 * Success: TRUE
2879 * Failure: FALSE
2881 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
2883 if (!infoPtr->hBkBrush) return FALSE;
2885 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
2887 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
2890 /***
2891 * DESCRIPTION:
2892 * Draws a subitem.
2894 * PARAMETER(S):
2895 * [I] infoPtr : valid pointer to the listview structure
2896 * [I] HDC : device context handle
2897 * [I] INT : item index
2898 * [I] INT : subitem index
2899 * [I] RECT * : clipping rectangle
2901 * RETURN:
2902 * Success: TRUE
2903 * Failure: FALSE
2905 static BOOL LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem,
2906 INT nSubItem, RECT rcItem, UINT align)
2908 WCHAR szDispText[DISP_TEXT_SIZE];
2909 LVITEMW lvItem;
2911 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, rcItem=%s)\n",
2912 hdc, nItem, nSubItem, debugrect(&rcItem));
2914 /* get information needed for drawing the item */
2915 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
2916 lvItem.iItem = nItem;
2917 lvItem.iSubItem = nSubItem;
2918 lvItem.cchTextMax = COUNTOF(szDispText);
2919 lvItem.pszText = szDispText;
2920 *lvItem.pszText = '\0';
2921 if (!LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE)) return FALSE;
2923 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2925 if (lvItem.iImage) FIXME("Draw the image for the subitem\n");
2927 DrawTextW(hdc, lvItem.pszText, -1, &rcItem,
2928 DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | align);
2930 return TRUE;
2934 /***
2935 * DESCRIPTION:
2936 * Draws an item.
2938 * PARAMETER(S):
2939 * [I] infoPtr : valid pointer to the listview structure
2940 * [I] hdc : device context handle
2941 * [I] nItem : item index
2942 * [I] rcItem : item rectangle
2944 * RETURN:
2945 * TRUE: if item is focused
2946 * FALSE: otherwise
2948 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
2950 WCHAR szDispText[DISP_TEXT_SIZE];
2951 INT nLabelWidth, imagePadding = 0;
2952 RECT* lprcFocus, rcOrig = rcItem;
2953 LVITEMW lvItem;
2954 TEXTATTR ta;
2956 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
2958 /* get information needed for drawing the item */
2959 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2960 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
2961 lvItem.iItem = nItem;
2962 lvItem.iSubItem = 0;
2963 lvItem.cchTextMax = DISP_TEXT_SIZE;
2964 lvItem.pszText = szDispText;
2965 *lvItem.pszText = '\0';
2966 if (!LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE)) return FALSE;
2967 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2969 /* now check if we need to update the focus rectangle */
2970 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
2971 if (lprcFocus) SetRectEmpty(lprcFocus);
2973 /* do indent */
2974 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
2976 /* state icons */
2977 if (infoPtr->himlState)
2979 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
2980 if (uStateImage)
2982 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
2983 rcItem.left, rcItem.top, ILD_NORMAL);
2985 rcItem.left += infoPtr->iconStateSize.cx;
2986 imagePadding = IMAGE_PADDING;
2989 /* small icons */
2990 if (infoPtr->himlSmall)
2992 if (lvItem.iImage >= 0)
2994 UINT mode = (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ?
2995 ILD_SELECTED : ILD_NORMAL;
2996 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2997 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc,
2998 rcItem.left, rcItem.top, mode);
3000 rcItem.left += infoPtr->iconSize.cx;
3001 imagePadding = IMAGE_PADDING;
3004 /* Don't bother painting item being edited */
3005 if (infoPtr->bEditing && lprcFocus)
3006 return FALSE;
3008 select_text_attr(infoPtr, hdc, lvItem.state & LVIS_SELECTED, &ta);
3010 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
3011 rcItem.left += imagePadding;
3012 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3013 if (rcItem.right > rcOrig.right) rcItem.right = rcOrig.right;
3015 if (lvItem.pszText)
3017 TRACE("drawing text=%s, in rect=%s\n", debugstr_w(lvItem.pszText), debugrect(&rcItem));
3018 if(lprcFocus) *lprcFocus = rcItem;
3019 if (lvItem.state & LVIS_SELECTED)
3020 ExtTextOutW(hdc, rcItem.left, rcItem.top, ETO_OPAQUE, &rcItem, 0, 0, 0);
3021 DrawTextW(hdc, lvItem.pszText, -1, &rcItem,
3022 DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | DT_CENTER);
3025 set_text_attr(hdc, &ta);
3026 return lprcFocus != NULL;
3029 /***
3030 * DESCRIPTION:
3031 * Draws an item when in large icon display mode.
3033 * PARAMETER(S):
3034 * [I] infoPtr : valid pointer to the listview structure
3035 * [I] hdc : device context handle
3036 * [I] nItem : item index
3037 * [I] rcItem : clipping rectangle
3039 * RETURN:
3040 * TRUE: if item is focused
3041 * FALSE: otherwise
3043 static BOOL LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
3045 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3046 LVITEMW lvItem;
3047 UINT uFormat = LISTVIEW_DTFLAGS;
3048 RECT rcIcon, rcFocus, rcFullText, rcLabel, *lprcFocus;
3049 POINT ptOrg;
3051 TRACE("(hdc=%x, nItem=%d, rcItem=%s)\n", hdc, nItem, debugrect(&rcItem));
3053 /* get information needed for drawing the item */
3054 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3055 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3056 lvItem.iItem = nItem;
3057 lvItem.iSubItem = 0;
3058 lvItem.cchTextMax = DISP_TEXT_SIZE;
3059 lvItem.pszText = szDispText;
3060 *lvItem.pszText = '\0';
3061 if (!LISTVIEW_GetItemW(infoPtr, &lvItem, FALSE)) return FALSE;
3062 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3064 /* now check if we need to update the focus rectangle */
3065 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3067 LISTVIEW_GetItemMeasures(infoPtr, nItem, &ptOrg, NULL, &rcIcon, &rcLabel, &rcFullText);
3069 /* Set the item to the boundary box for now */
3070 TRACE("iconSize.cx=%ld, nItemWidth=%d\n", infoPtr->iconSize.cx, infoPtr->nItemWidth);
3071 TRACE("rcList=%s, rcView=%s\n", debugrect(&infoPtr->rcList), debugrect(&infoPtr->rcView));
3073 /* Figure out text colours etc. depending on state
3074 * At least the following states exist; there may be more.
3075 * Many items may be selected
3076 * At most one item may have the focus
3077 * The application may not actually be active currently
3078 * 1. The item is not selected in any way
3079 * 2. The cursor is flying over the icon or text and the text is being
3080 * expanded because it is not fully displayed currently.
3081 * 3. The item is selected and is focussed, i.e. the user has not clicked
3082 * in the blank area of the window, and the window (or application?)
3083 * still has the focus.
3084 * 4. As 3 except that a different window has the focus
3085 * 5. The item is the selected item of all the items, but the user has
3086 * clicked somewhere else on the window.
3087 * Only a few of these are handled currently. In particular 2 is not yet
3088 * handled since we do not support the functionality currently (or at least
3089 * we didn't when I wrote this)
3092 if (lvItem.state & LVIS_SELECTED)
3094 /* set item colors */
3095 SetBkColor(hdc, comctl32_color.clrHighlight);
3096 SetTextColor(hdc, comctl32_color.clrHighlightText);
3097 SetBkMode (hdc, OPAQUE);
3098 /* set raster mode */
3099 SetROP2(hdc, R2_XORPEN);
3100 /* When exactly is it in XOR? while being dragged? */
3102 else
3104 /* set item colors */
3105 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3107 SetBkMode(hdc, TRANSPARENT);
3109 else
3111 SetBkMode(hdc, OPAQUE);
3112 SetBkColor(hdc, infoPtr->clrTextBk);
3115 SetTextColor(hdc, infoPtr->clrText);
3116 /* set raster mode */
3117 SetROP2(hdc, R2_COPYPEN);
3120 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3121 * wrapping and long words split.
3122 * In cases 1 and 4 only a portion of the text is displayed with word
3123 * wrapping and both word and end ellipsis. (I don't yet know about path
3124 * ellipsis)
3126 uFormat |= lprcFocus ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3128 /* state icons */
3129 if (infoPtr->himlState != NULL)
3131 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3132 INT x, y;
3134 x = rcIcon.left - infoPtr->iconStateSize.cx + 10;
3135 y = rcIcon.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
3136 if (uStateImage > 0)
3138 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, x,
3139 y, ILD_NORMAL);
3143 /* draw the icon */
3144 if (infoPtr->himlNormal != NULL)
3146 if (lvItem.iImage >= 0)
3148 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc,
3149 rcIcon.left+ICON_LR_HALF,
3150 rcIcon.top+ICON_TOP_PADDING_HITABLE,
3151 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3152 TRACE("icon %d at (%d,%d)\n",
3153 lvItem.iImage, rcIcon.left+ICON_LR_HALF,
3154 rcIcon.top+ICON_TOP_PADDING_HITABLE);
3158 /* Draw the text below the icon */
3160 /* Don't bother painting item being edited */
3161 if ((infoPtr->bEditing && lprcFocus) || !lvItem.pszText || !lstrlenW(lvItem.pszText))
3163 if(lprcFocus) SetRectEmpty(lprcFocus);
3164 return FALSE;
3167 /* draw label */
3169 /* I am sure of most of the uFormat values. However I am not sure about
3170 * whether we need or do not need the following:
3171 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3172 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3173 * We certainly do not need
3174 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3175 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3178 /* If the text is being drawn without clipping (i.e. the full text) then we
3179 * need to jump through a few hoops to ensure that it all gets displayed and
3180 * that the background is complete
3182 rcFocus = rcLabel; /* save for focus */
3183 SetRectEmpty(&infoPtr->rcLargeFocus);
3184 if ((uFormat & DT_NOCLIP) || (lvItem.state & LVIS_SELECTED))
3186 /* FIXME: why do we need this??? */
3187 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3189 FillRect(hdc, &rcFullText, hBrush);
3190 rcFocus = rcFullText;
3191 DeleteObject(hBrush);
3193 /* Save size of item drawing for next InvalidateRect */
3194 infoPtr->rcLargeFocus = rcFullText;
3195 TRACE("focused/selected, rcFocus=%s\n", debugrect(&rcFocus));
3197 /* else ? What if we are losing the focus? will we not get a complete
3198 * background?
3201 DrawTextW (hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3202 TRACE("text at rcLabel=%s is %s\n", debugrect(&rcLabel), debugstr_w(lvItem.pszText));
3204 if(lprcFocus) CopyRect(lprcFocus, &rcFocus);
3206 return lprcFocus != NULL;
3209 /***
3210 * DESCRIPTION:
3211 * Draws listview items when in report display mode.
3213 * PARAMETER(S):
3214 * [I] infoPtr : valid pointer to the listview structure
3215 * [I] HDC : device context handle
3217 * RETURN:
3218 * None
3220 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3222 INT rgntype, nDrawPosY, j;
3223 INT nTop, nItem, nLast, nUpdateHeight, nUpdateWidth;
3224 INT nColumnCount, nFirstCol, nLastCol;
3225 RECT rcItem, rcClip, rcFullSelect;
3226 BOOL bFullSelected, isFocused;
3227 DWORD cditemmode = CDRF_DODEFAULT;
3228 LONG lStyle = infoPtr->dwStyle;
3229 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3230 TEXTATTR tmpTa, oldTa;
3231 COLUMNCACHE *lpCols;
3232 LVCOLUMNW lvColumn;
3233 LVITEMW lvItem;
3234 POINT ptOrig;
3236 TRACE("()\n");
3238 /* nothing to draw */
3239 if(GETITEMCOUNT(infoPtr) == 0) return;
3241 /* figure out what to draw */
3242 rgntype = GetClipBox(hdc, &rcClip);
3243 if (rgntype == NULLREGION) return;
3244 nUpdateHeight = rcClip.bottom - rcClip.top + 1;
3245 nUpdateWidth = rcClip.right - rcClip.left;
3246 nTop = LISTVIEW_GetTopIndex(infoPtr);
3247 nItem = nTop + (rcClip.top - infoPtr->rcList.top) / infoPtr->nItemHeight;
3248 if (nItem < nTop)
3249 nItem = nTop;
3250 nLast = nItem + nUpdateHeight / infoPtr->nItemHeight;
3251 if (nUpdateHeight % infoPtr->nItemHeight) nLast++;
3252 if (nLast > GETITEMCOUNT(infoPtr))
3253 nLast = GETITEMCOUNT(infoPtr);
3255 /* send cache hint notification */
3256 if (lStyle & LVS_OWNERDATA)
3257 notify_odcachehint(infoPtr, nItem, nLast);
3259 /* cache column info */
3260 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3261 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3262 if (!lpCols) return;
3263 for (j = 0; j < nColumnCount; j++)
3265 Header_GetItemRect(infoPtr->hwndHeader, j, &lpCols[j].rc);
3266 TRACE("lpCols[%d].rc=%s\n", j, debugrect(&lpCols[j].rc));
3269 /* Get scroll info once before loop */
3270 LISTVIEW_GetOrigin(infoPtr, &ptOrig);
3272 /* we now narrow the columns as well */
3273 nLastCol = nColumnCount - 1;
3274 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3275 if (lpCols[nFirstCol].rc.right + ptOrig.x >= rcClip.left) break;
3276 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3277 if (lpCols[nLastCol].rc.left + ptOrig.x < rcClip.right) break;
3279 /* cache the per-column information before we start drawing */
3280 for (j = nFirstCol; j <= nLastCol; j++)
3282 lvColumn.mask = LVCF_FMT;
3283 LISTVIEW_GetColumnT(infoPtr, j, &lvColumn, TRUE);
3284 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3285 lpCols[j].align = DT_LEFT;
3286 if (lvColumn.fmt & LVCFMT_RIGHT)
3287 lpCols[j].align = DT_RIGHT;
3288 else if (lvColumn.fmt & LVCFMT_CENTER)
3289 lpCols[j].align = DT_CENTER;
3292 /* a last few bits before we start drawing */
3293 TRACE("nTop=%d, nItem=%d, nLast=%d, nFirstCol=%d, nLastCol=%d\n",
3294 nTop, nItem, nLast, nFirstCol, nLastCol);
3295 bFullSelected = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT;
3296 nDrawPosY = infoPtr->rcList.top + (nItem - nTop) * infoPtr->nItemHeight;
3298 /* save dc values we're gonna trash while drawing */
3299 oldTa.bkMode = GetBkMode(hdc);
3300 oldTa.bkColor = GetBkColor(hdc);
3301 oldTa.fgColor = GetTextColor(hdc);
3303 /* iterate through the invalidated rows */
3304 for (; nItem < nLast; nItem++, nDrawPosY += infoPtr->nItemHeight)
3306 /* if owner wants to take a first stab at it, have it his way... */
3307 if (lStyle & LVS_OWNERDRAWFIXED)
3309 DRAWITEMSTRUCT dis;
3310 LVITEMW item;
3312 TRACE("Owner Drawn\n");
3314 item.iItem = nItem;
3315 item.iSubItem = 0;
3316 item.mask = LVIF_PARAM | LVIF_STATE;
3317 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3318 if (!LISTVIEW_GetItemW(infoPtr, &item, TRUE)) continue;
3320 dis.hwndItem = infoPtr->hwndSelf;
3321 dis.hDC = hdc;
3322 dis.CtlType = ODT_LISTVIEW;
3323 dis.CtlID = uID;
3324 dis.itemID = nItem;
3325 dis.itemAction = ODA_DRAWENTIRE;
3326 dis.itemData = item.lParam;
3327 dis.itemState = 0;
3328 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3329 if (item.state & LVIS_FOCUSED) dis.itemState |= ODS_FOCUS;
3330 dis.rcItem.left = lpCols[0].rc.left;
3331 dis.rcItem.right = lpCols[nColumnCount - 1].rc.right;
3332 dis.rcItem.top = nDrawPosY;
3333 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3334 OffsetRect(&dis.rcItem, ptOrig.x, 0);
3336 if (SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, uID, (LPARAM)&dis))
3337 continue;
3340 /* compute the full select rectangle, if needed */
3341 if (bFullSelected)
3343 lvItem.mask = LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3344 lvItem.stateMask = LVIS_SELECTED;
3345 lvItem.iItem = nItem;
3346 lvItem.iSubItem = 0;
3347 if (!LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE)) continue;
3349 rcFullSelect.left = lpCols[0].rc.left + REPORT_MARGINX +
3350 infoPtr->iconSize.cx * lvItem.iIndent +
3351 (infoPtr->himlSmall ? infoPtr->iconSize.cx : 0);
3352 rcFullSelect.right = max(rcFullSelect.left, lpCols[nColumnCount - 1].rc.right - REPORT_MARGINX);
3353 rcFullSelect.top = nDrawPosY;
3354 rcFullSelect.bottom = rcFullSelect.top + infoPtr->nItemHeight;
3355 OffsetRect(&rcFullSelect, ptOrig.x, 0);
3358 /* draw the background of the selection rectangle, if need be */
3359 select_text_attr(infoPtr, hdc, bFullSelected && (lvItem.state & LVIS_SELECTED), &tmpTa);
3360 if (bFullSelected && (lvItem.state & LVIS_SELECTED))
3361 ExtTextOutW(hdc, rcFullSelect.left, rcFullSelect.top, ETO_OPAQUE, &rcFullSelect, 0, 0, 0);
3363 /* iterate through the invalidated columns */
3364 isFocused = FALSE;
3365 for (j = nFirstCol; j <= nLastCol; j++)
3367 if (cdmode & CDRF_NOTIFYITEMDRAW)
3368 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, j, CDDS_ITEMPREPAINT);
3369 if (cditemmode & CDRF_SKIPDEFAULT) continue;
3371 rcItem = lpCols[j].rc;
3372 rcItem.left += REPORT_MARGINX;
3373 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3374 rcItem.top = nDrawPosY;
3375 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3377 /* Offset the Scroll Bar Pos */
3378 OffsetRect(&rcItem, ptOrig.x, 0);
3380 if (j == 0)
3381 isFocused = LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3382 else
3383 LISTVIEW_DrawSubItem(infoPtr, hdc, nItem, j, rcItem, lpCols[j].align);
3385 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3386 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3389 /* Adjust focus if we have it, and we are in full select */
3390 if (bFullSelected && isFocused) infoPtr->rcFocus = rcFullSelect;
3393 /* cleanup the mess */
3394 set_text_attr(hdc, &oldTa);
3395 COMCTL32_Free(lpCols);
3398 /***
3399 * DESCRIPTION:
3400 * Draws listview items when in list display mode.
3402 * PARAMETER(S):
3403 * [I] infoPtr : valid pointer to the listview structure
3404 * [I] HDC : device context handle
3406 * RETURN:
3407 * None
3409 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3411 RECT rcItem;
3412 INT i, j;
3413 INT nItem;
3414 INT nColumnCount;
3415 INT nCountPerColumn;
3416 INT nItemWidth = infoPtr->nItemWidth;
3417 INT nItemHeight = infoPtr->nItemHeight;
3418 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3419 DWORD cditemmode = CDRF_DODEFAULT;
3421 /* get number of fully visible columns */
3422 nColumnCount = nListWidth / nItemWidth;
3423 if (nListWidth % nItemWidth) nColumnCount++;
3424 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
3425 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3426 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3427 nColumnCount, nCountPerColumn, nItem);
3429 /* nothing to draw, return here */
3430 if(GETITEMCOUNT(infoPtr) == 0)
3431 return;
3433 for (i = 0; i < nColumnCount; i++)
3435 for (j = 0; j < nCountPerColumn; j++, nItem++)
3437 if (nItem >= GETITEMCOUNT(infoPtr))
3438 return;
3440 if (cdmode & CDRF_NOTIFYITEMDRAW)
3441 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, 0, CDDS_ITEMPREPAINT);
3442 if (cditemmode & CDRF_SKIPDEFAULT)
3443 continue;
3445 rcItem.top = j * nItemHeight;
3446 rcItem.left = i * nItemWidth;
3447 rcItem.bottom = rcItem.top + nItemHeight;
3448 rcItem.right = rcItem.left + nItemWidth;
3450 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3452 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3453 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3459 /***
3460 * DESCRIPTION:
3461 * Draws listview items when in icon or small icon display mode.
3463 * PARAMETER(S):
3464 * [I] infoPtr : valid pointer to the listview structure
3465 * [I] HDC : device context handle
3467 * RETURN:
3468 * None
3470 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL bSmall, DWORD cdmode)
3472 POINT ptPosition;
3473 POINT ptOrigin;
3474 RECT rcItem, rcClip, rcTemp;
3475 INT i;
3476 DWORD cditemmode = CDRF_DODEFAULT;
3478 TRACE("\n");
3480 /* nothing to draw, return here */
3481 if(GETITEMCOUNT(infoPtr) == 0)
3482 return;
3484 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
3486 GetClipBox(hdc, &rcClip);
3488 /* Draw the visible non-selected items */
3489 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3491 if (LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3492 continue;
3494 rcItem.left = LVIR_BOUNDS;
3495 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3496 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3497 continue;
3499 if (cdmode & CDRF_NOTIFYITEMDRAW)
3500 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3501 if (cditemmode & CDRF_SKIPDEFAULT)
3502 continue;
3504 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3505 ptPosition.x += ptOrigin.x;
3506 ptPosition.y += ptOrigin.y;
3508 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3510 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3512 if (ptPosition.y < infoPtr->rcList.bottom)
3514 if (ptPosition.x < infoPtr->rcList.right)
3516 rcItem.top = ptPosition.y;
3517 rcItem.left = ptPosition.x;
3518 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3519 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3521 if (bSmall)
3522 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3523 else
3524 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3529 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3530 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3533 /* Draw the visible selected items */
3534 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3536 if (!LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3537 continue;
3539 rcItem.left = LVIR_BOUNDS;
3540 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3541 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3542 continue;
3544 if (cdmode & CDRF_NOTIFYITEMDRAW)
3545 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3546 if (cditemmode & CDRF_SKIPDEFAULT)
3547 continue;
3549 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3550 ptPosition.x += ptOrigin.x;
3551 ptPosition.y += ptOrigin.y;
3553 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3555 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3557 if (ptPosition.y < infoPtr->rcList.bottom)
3559 if (ptPosition.x < infoPtr->rcList.right)
3561 rcItem.top = ptPosition.y;
3562 rcItem.left = ptPosition.x;
3563 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3564 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3566 if (bSmall)
3567 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3568 else
3569 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3574 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3575 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3579 /***
3580 * DESCRIPTION:
3581 * Draws listview items.
3583 * PARAMETER(S):
3584 * [I] infoPtr : valid pointer to the listview structure
3585 * [I] HDC : device context handle
3587 * RETURN:
3588 * NoneX
3590 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3592 UINT uView = LISTVIEW_GetType(infoPtr);
3593 HFONT hOldFont;
3594 DWORD cdmode;
3595 RECT rcClient;
3597 LISTVIEW_DUMP(infoPtr);
3599 GetClientRect(infoPtr->hwndSelf, &rcClient);
3601 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, hdc, rcClient);
3602 if (cdmode == CDRF_SKIPDEFAULT) return;
3604 infoPtr->bIsDrawing = TRUE;
3606 /* select font */
3607 hOldFont = SelectObject(hdc, infoPtr->hFont);
3609 if (uView == LVS_LIST)
3610 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3611 else if (uView == LVS_REPORT)
3612 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3613 else
3614 LISTVIEW_RefreshIcon(infoPtr, hdc, uView == LVS_SMALLICON, cdmode);
3616 /* if we have a focus rect, draw it */
3617 if (infoPtr->bFocus && !IsRectEmpty(&infoPtr->rcFocus))
3618 DrawFocusRect(hdc, &infoPtr->rcFocus);
3620 /* unselect objects */
3621 SelectObject(hdc, hOldFont);
3623 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3624 notify_customdraw(infoPtr, CDDS_POSTPAINT, hdc, rcClient);
3626 infoPtr->bIsDrawing = FALSE;
3630 /***
3631 * DESCRIPTION:
3632 * Calculates the approximate width and height of a given number of items.
3634 * PARAMETER(S):
3635 * [I] infoPtr : valid pointer to the listview structure
3636 * [I] INT : number of items
3637 * [I] INT : width
3638 * [I] INT : height
3640 * RETURN:
3641 * Returns a DWORD. The width in the low word and the height in high word.
3643 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3644 WORD wWidth, WORD wHeight)
3646 UINT uView = LISTVIEW_GetType(infoPtr);
3647 INT nItemCountPerColumn = 1;
3648 INT nColumnCount = 0;
3649 DWORD dwViewRect = 0;
3651 if (nItemCount == -1)
3652 nItemCount = GETITEMCOUNT(infoPtr);
3654 if (uView == LVS_LIST)
3656 if (wHeight == 0xFFFF)
3658 /* use current height */
3659 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3662 if (wHeight < infoPtr->nItemHeight)
3663 wHeight = infoPtr->nItemHeight;
3665 if (nItemCount > 0)
3667 if (infoPtr->nItemHeight > 0)
3669 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3670 if (nItemCountPerColumn == 0)
3671 nItemCountPerColumn = 1;
3673 if (nItemCount % nItemCountPerColumn != 0)
3674 nColumnCount = nItemCount / nItemCountPerColumn;
3675 else
3676 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3680 /* Microsoft padding magic */
3681 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3682 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3684 dwViewRect = MAKELONG(wWidth, wHeight);
3686 else if (uView == LVS_REPORT)
3687 FIXME("uView == LVS_REPORT: not implemented\n");
3688 else if (uView == LVS_SMALLICON)
3689 FIXME("uView == LVS_SMALLICON: not implemented\n");
3690 else if (uView == LVS_ICON)
3691 FIXME("uView == LVS_ICON: not implemented\n");
3693 return dwViewRect;
3696 /***
3697 * DESCRIPTION:
3698 * Arranges listview items in icon display mode.
3700 * PARAMETER(S):
3701 * [I] infoPtr : valid pointer to the listview structure
3702 * [I] INT : alignment code
3704 * RETURN:
3705 * SUCCESS : TRUE
3706 * FAILURE : FALSE
3708 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3710 UINT uView = LISTVIEW_GetType(infoPtr);
3711 BOOL bResult = FALSE;
3713 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3715 switch (nAlignCode)
3717 case LVA_ALIGNLEFT:
3718 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3719 break;
3720 case LVA_ALIGNTOP:
3721 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3722 break;
3723 case LVA_DEFAULT:
3724 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3725 break;
3726 case LVA_SNAPTOGRID:
3727 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3728 break;
3732 return bResult;
3735 /* << LISTVIEW_CreateDragImage >> */
3738 /***
3739 * DESCRIPTION:
3740 * Removes all listview items and subitems.
3742 * PARAMETER(S):
3743 * [I] infoPtr : valid pointer to the listview structure
3745 * RETURN:
3746 * SUCCESS : TRUE
3747 * FAILURE : FALSE
3749 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3751 LONG lStyle = infoPtr->dwStyle;
3752 UINT uView = lStyle & LVS_TYPEMASK;
3753 LISTVIEW_ITEM *lpItem;
3754 LISTVIEW_SUBITEM *lpSubItem;
3755 NMLISTVIEW nmlv;
3756 BOOL bSuppress;
3757 BOOL bResult = FALSE;
3758 HDPA hdpaSubItems;
3760 TRACE("()\n");
3762 LISTVIEW_RemoveAllSelections(infoPtr);
3763 infoPtr->nSelectionMark=-1;
3764 infoPtr->nFocusedItem=-1;
3765 /* But we are supposed to leave nHotItem as is! */
3767 if (lStyle & LVS_OWNERDATA)
3769 infoPtr->hdpaItems->nItemCount = 0;
3770 LISTVIEW_InvalidateList(infoPtr);
3771 return TRUE;
3774 if (GETITEMCOUNT(infoPtr) > 0)
3776 INT i, j;
3778 /* send LVN_DELETEALLITEMS notification */
3779 /* verify if subsequent LVN_DELETEITEM notifications should be
3780 suppressed */
3781 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3782 nmlv.iItem = -1;
3783 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3785 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3787 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3788 if (hdpaSubItems != NULL)
3790 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3792 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3793 if (lpSubItem != NULL)
3795 /* free subitem string */
3796 if (is_textW(lpSubItem->hdr.pszText))
3797 COMCTL32_Free(lpSubItem->hdr.pszText);
3799 /* free subitem */
3800 COMCTL32_Free(lpSubItem);
3804 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3805 if (lpItem != NULL)
3807 if (!bSuppress)
3809 /* send LVN_DELETEITEM notification */
3810 nmlv.iItem = i;
3811 nmlv.lParam = lpItem->lParam;
3812 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3815 /* free item string */
3816 if (is_textW(lpItem->hdr.pszText))
3817 COMCTL32_Free(lpItem->hdr.pszText);
3819 /* free item */
3820 COMCTL32_Free(lpItem);
3823 DPA_Destroy(hdpaSubItems);
3827 /* reinitialize listview memory */
3828 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3830 /* align items (set position of each item) */
3831 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3833 if (lStyle & LVS_ALIGNLEFT)
3835 LISTVIEW_AlignLeft(infoPtr);
3837 else
3839 LISTVIEW_AlignTop(infoPtr);
3843 LISTVIEW_UpdateScroll(infoPtr);
3845 /* invalidate client area (optimization needed) */
3846 LISTVIEW_InvalidateList(infoPtr);
3849 return bResult;
3852 /***
3853 * DESCRIPTION:
3854 * Removes a column from the listview control.
3856 * PARAMETER(S):
3857 * [I] infoPtr : valid pointer to the listview structure
3858 * [I] INT : column index
3860 * RETURN:
3861 * SUCCESS : TRUE
3862 * FAILURE : FALSE
3864 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3866 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3867 RECT rcCol, rcOld;
3869 TRACE("nColumn=%d\n", nColumn);
3871 if (nColumn <= 0) return FALSE;
3873 if (uView == LVS_REPORT)
3875 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3876 return FALSE;
3878 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3879 return FALSE;
3882 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3884 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3885 HDPA hdpaSubItems;
3886 INT nItem, nSubItem, i;
3888 for (nItem = 0; nItem < infoPtr->hdpaItems->nItemCount; nItem++)
3890 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3891 if (!hdpaSubItems) continue;
3892 nSubItem = 0;
3893 lpDelItem = 0;
3894 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3896 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3897 if (!lpSubItem) break;
3898 if (lpSubItem->iSubItem == nColumn)
3900 nSubItem = i;
3901 lpDelItem = lpSubItem;
3903 else if (lpSubItem->iSubItem > nColumn)
3905 lpSubItem->iSubItem--;
3909 /* if we found our subitem, zapp it */
3910 if (nSubItem > 0)
3912 /* free string */
3913 if (is_textW(lpDelItem->hdr.pszText))
3914 COMCTL32_Free(lpDelItem->hdr.pszText);
3916 /* free item */
3917 COMCTL32_Free(lpDelItem);
3919 /* free dpa memory */
3920 DPA_DeletePtr(hdpaSubItems, nSubItem);
3925 /* we need to worry about display issues in report mode only */
3926 if (uView != LVS_REPORT) return TRUE;
3928 /* Need to reset the item width when deleting a column */
3929 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
3931 /* update scrollbar(s) */
3932 LISTVIEW_UpdateScroll(infoPtr);
3934 /* scroll to cover the deleted column, and invalidate for redraw */
3935 rcOld = infoPtr->rcList;
3936 rcOld.left = rcCol.left;
3937 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
3938 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3940 return TRUE;
3943 /***
3944 * DESCRIPTION:
3945 * Removes an item from the listview control.
3947 * PARAMETER(S):
3948 * [I] infoPtr : valid pointer to the listview structure
3949 * [I] INT : item index
3951 * RETURN:
3952 * SUCCESS : TRUE
3953 * FAILURE : FALSE
3955 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3957 LONG lStyle = infoPtr->dwStyle;
3958 UINT uView = lStyle & LVS_TYPEMASK;
3959 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3960 NMLISTVIEW nmlv;
3961 BOOL bResult = FALSE;
3962 HDPA hdpaSubItems;
3963 LISTVIEW_ITEM *lpItem;
3964 LISTVIEW_SUBITEM *lpSubItem;
3965 INT i;
3966 LVITEMW item;
3968 TRACE("(nItem=%d)\n", nItem);
3971 /* First, send LVN_DELETEITEM notification. */
3972 memset(&nmlv, 0, sizeof (NMLISTVIEW));
3973 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
3974 nmlv.hdr.idFrom = lCtrlId;
3975 nmlv.hdr.code = LVN_DELETEITEM;
3976 nmlv.iItem = nItem;
3977 SendMessageW((infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
3978 (LPARAM)&nmlv);
3981 /* remove it from the selection range */
3982 item.state = LVIS_SELECTED;
3983 item.stateMask = LVIS_SELECTED;
3984 LISTVIEW_SetItemState(infoPtr,nItem,&item);
3986 if (lStyle & LVS_OWNERDATA)
3988 infoPtr->hdpaItems->nItemCount --;
3989 LISTVIEW_InvalidateList(infoPtr);
3990 return TRUE;
3993 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
3995 /* initialize memory */
3996 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3998 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
3999 if (hdpaSubItems != NULL)
4001 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4003 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4004 if (lpSubItem != NULL)
4006 /* free item string */
4007 if (is_textW(lpSubItem->hdr.pszText))
4008 COMCTL32_Free(lpSubItem->hdr.pszText);
4010 /* free item */
4011 COMCTL32_Free(lpSubItem);
4015 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4016 if (lpItem != NULL)
4018 /* free item string */
4019 if (is_textW(lpItem->hdr.pszText))
4020 COMCTL32_Free(lpItem->hdr.pszText);
4022 /* free item */
4023 COMCTL32_Free(lpItem);
4026 bResult = DPA_Destroy(hdpaSubItems);
4029 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
4031 /* align items (set position of each item) */
4032 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4034 if (lStyle & LVS_ALIGNLEFT)
4035 LISTVIEW_AlignLeft(infoPtr);
4036 else
4037 LISTVIEW_AlignTop(infoPtr);
4040 LISTVIEW_UpdateScroll(infoPtr);
4042 /* FIXME: optimizartion refresh client area */
4043 LISTVIEW_InvalidateList(infoPtr);
4046 return bResult;
4050 /***
4051 * DESCRIPTION:
4052 * Callback implementation for editlabel control
4054 * PARAMETER(S):
4055 * [I] infoPtr : valid pointer to the listview structure
4056 * [I] pszText : modified text
4057 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4059 * RETURN:
4060 * SUCCESS : TRUE
4061 * FAILURE : FALSE
4063 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4065 NMLVDISPINFOW dispInfo;
4067 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4069 infoPtr->bEditing = FALSE;
4071 ZeroMemory(&dispInfo, sizeof(dispInfo));
4072 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4073 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4074 dispInfo.item.iSubItem = 0;
4075 dispInfo.item.stateMask = ~0;
4076 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item, TRUE)) return FALSE;
4077 dispInfo.item.pszText = pszText;
4078 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4080 /* Do we need to update the Item Text */
4081 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4082 if (!pszText) return TRUE;
4084 ZeroMemory(&dispInfo, sizeof(dispInfo));
4085 dispInfo.item.mask = LVIF_TEXT;
4086 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4087 dispInfo.item.iSubItem = 0;
4088 dispInfo.item.pszText = pszText;
4089 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4090 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4093 /***
4094 * DESCRIPTION:
4095 * Begin in place editing of specified list view item
4097 * PARAMETER(S):
4098 * [I] infoPtr : valid pointer to the listview structure
4099 * [I] INT : item index
4100 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4102 * RETURN:
4103 * SUCCESS : TRUE
4104 * FAILURE : FALSE
4106 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4108 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4109 NMLVDISPINFOW dispInfo;
4110 RECT rect;
4112 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4114 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4116 infoPtr->nEditLabelItem = nItem;
4118 /* Is the EditBox still there, if so remove it */
4119 if(infoPtr->hwndEdit != 0)
4121 SetFocus(infoPtr->hwndSelf);
4122 infoPtr->hwndEdit = 0;
4125 LISTVIEW_SetSelection(infoPtr, nItem);
4126 LISTVIEW_SetItemFocus(infoPtr, nItem);
4128 rect.left = LVIR_LABEL;
4129 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4131 ZeroMemory(&dispInfo, sizeof(dispInfo));
4132 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4133 dispInfo.item.iItem = nItem;
4134 dispInfo.item.iSubItem = 0;
4135 dispInfo.item.stateMask = ~0;
4136 dispInfo.item.pszText = szDispText;
4137 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4138 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, FALSE, isW)) return 0;
4140 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4141 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4142 if (!infoPtr->hwndEdit) return 0;
4144 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4146 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4147 infoPtr->hwndEdit = 0;
4148 return 0;
4151 infoPtr->bEditing = TRUE;
4152 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4153 SetFocus(infoPtr->hwndEdit);
4154 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4155 return infoPtr->hwndEdit;
4159 /***
4160 * DESCRIPTION:
4161 * Ensures the specified item is visible, scrolling into view if necessary.
4163 * PARAMETER(S):
4164 * [I] infoPtr : valid pointer to the listview structure
4165 * [I] nItem : item index
4166 * [I] bPartial : partially or entirely visible
4168 * RETURN:
4169 * SUCCESS : TRUE
4170 * FAILURE : FALSE
4172 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4174 UINT uView = LISTVIEW_GetType(infoPtr);
4175 INT nScrollPosHeight = 0;
4176 INT nScrollPosWidth = 0;
4177 INT nPartialAdjust = 0;
4178 INT nHorzDiff = 0;
4179 INT nVertDiff = 0;
4180 RECT rcItem;
4182 /* FIXME: ALWAYS bPartial == FALSE, FOR NOW! */
4184 rcItem.left = LVIR_BOUNDS;
4185 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4187 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4189 /* scroll left/right, but in LVS_REPORT mode */
4190 if (uView == LVS_LIST)
4191 nScrollPosWidth = infoPtr->nItemWidth;
4192 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4193 nScrollPosWidth = 1;
4195 if (rcItem.left < infoPtr->rcList.left)
4197 nPartialAdjust = -1;
4198 if (uView != LVS_REPORT) nHorzDiff = rcItem.left + infoPtr->rcList.left;
4200 else
4202 nPartialAdjust = 1;
4203 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4207 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4209 /* scroll up/down, but not in LVS_LIST mode */
4210 if (uView == LVS_REPORT)
4211 nScrollPosHeight = infoPtr->nItemHeight;
4212 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4213 nScrollPosHeight = 1;
4215 if (rcItem.top < infoPtr->rcList.top)
4217 nPartialAdjust = -1;
4218 if (uView != LVS_LIST) nVertDiff = rcItem.top + infoPtr->rcList.top;
4220 else
4222 nPartialAdjust = 1;
4223 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4227 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4229 if (nScrollPosWidth)
4231 INT diff = nHorzDiff / nScrollPosWidth;
4232 if (rcItem.left % nScrollPosWidth) diff += nPartialAdjust;
4233 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4236 if (nScrollPosHeight)
4238 INT diff = nVertDiff / nScrollPosHeight;
4239 if (rcItem.top % nScrollPosHeight) diff += nPartialAdjust;
4240 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4243 return TRUE;
4246 /***
4247 * DESCRIPTION:
4248 * Retrieves the nearest item, given a position and a direction.
4250 * PARAMETER(S):
4251 * [I] infoPtr : valid pointer to the listview structure
4252 * [I] POINT : start position
4253 * [I] UINT : direction
4255 * RETURN:
4256 * Item index if successdful, -1 otherwise.
4258 static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDirection)
4260 LVHITTESTINFO ht;
4261 RECT rcView;
4263 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4264 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4265 ((vkDirection == VK_UP) ? "VK_UP" :
4266 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4268 if (!LISTVIEW_GetViewRect(infoPtr, &rcView)) return -1;
4270 if (!LISTVIEW_GetOrigin(infoPtr, &ht.pt)) return -1;
4272 ht.pt.x += pt.x;
4273 ht.pt.y += pt.y;
4275 if (vkDirection == VK_DOWN) ht.pt.y += infoPtr->nItemHeight;
4276 else if (vkDirection == VK_UP) ht.pt.y -= infoPtr->nItemHeight;
4277 else if (vkDirection == VK_LEFT) ht.pt.x -= infoPtr->nItemWidth;
4278 else if (vkDirection == VK_RIGHT) ht.pt.x += infoPtr->nItemWidth;
4280 if (!PtInRect(&rcView, ht.pt)) return -1;
4282 return LISTVIEW_SuperHitTestItem(infoPtr, &ht, TRUE, TRUE);
4285 /***
4286 * DESCRIPTION:
4287 * Searches for an item with specific characteristics.
4289 * PARAMETER(S):
4290 * [I] hwnd : window handle
4291 * [I] nStart : base item index
4292 * [I] lpFindInfo : item information to look for
4294 * RETURN:
4295 * SUCCESS : index of item
4296 * FAILURE : -1
4298 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4299 LPLVFINDINFOW lpFindInfo)
4301 POINT ptItem;
4302 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4303 LVITEMW lvItem;
4304 BOOL bWrap = FALSE;
4305 INT nItem = nStart;
4306 INT nLast = GETITEMCOUNT(infoPtr);
4308 if ((nItem >= -1) && (lpFindInfo != NULL))
4310 lvItem.mask = 0;
4311 if (lpFindInfo->flags & LVFI_PARAM)
4313 lvItem.mask |= LVIF_PARAM;
4316 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4318 lvItem.mask |= LVIF_TEXT;
4319 lvItem.pszText = szDispText;
4320 lvItem.cchTextMax = DISP_TEXT_SIZE;
4323 if (lpFindInfo->flags & LVFI_WRAP)
4324 bWrap = TRUE;
4326 if (lpFindInfo->flags & LVFI_NEARESTXY)
4328 ptItem.x = lpFindInfo->pt.x;
4329 ptItem.y = lpFindInfo->pt.y;
4332 while (1)
4334 while (nItem < nLast)
4336 if (lpFindInfo->flags & LVFI_NEARESTXY)
4338 nItem = LISTVIEW_GetNearestItem(infoPtr, ptItem,
4339 lpFindInfo->vkDirection);
4340 if (nItem != -1)
4342 /* get position of the new item index */
4343 if (!ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &ptItem))
4344 return -1;
4346 else
4347 return -1;
4349 else
4351 nItem++;
4354 lvItem.iItem = nItem;
4355 lvItem.iSubItem = 0;
4356 if (LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE))
4358 if (lvItem.mask & LVIF_TEXT)
4360 if (lpFindInfo->flags & LVFI_PARTIAL)
4362 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4363 continue;
4365 else
4367 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4368 continue;
4372 if (lvItem.mask & LVIF_PARAM)
4374 if (lpFindInfo->lParam != lvItem.lParam)
4375 continue;
4378 return nItem;
4382 if (bWrap)
4384 nItem = -1;
4385 nLast = nStart + 1;
4386 bWrap = FALSE;
4388 else
4390 return -1;
4395 return -1;
4398 /***
4399 * DESCRIPTION:
4400 * Searches for an item with specific characteristics.
4402 * PARAMETER(S):
4403 * [I] hwnd : window handle
4404 * [I] nStart : base item index
4405 * [I] lpFindInfo : item information to look for
4407 * RETURN:
4408 * SUCCESS : index of item
4409 * FAILURE : -1
4411 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4412 LPLVFINDINFOA lpFindInfo)
4414 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4415 LVFINDINFOW fiw;
4416 LRESULT res;
4418 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4419 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4420 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4421 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4422 return res;
4425 /***
4426 * DESCRIPTION:
4427 * Retrieves the background image of the listview control.
4429 * PARAMETER(S):
4430 * [I] infoPtr : valid pointer to the listview structure
4431 * [O] LPLVMKBIMAGE : background image attributes
4433 * RETURN:
4434 * SUCCESS : TRUE
4435 * FAILURE : FALSE
4437 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4438 /* { */
4439 /* FIXME (listview, "empty stub!\n"); */
4440 /* return FALSE; */
4441 /* } */
4443 /***
4444 * DESCRIPTION:
4445 * Retrieves column attributes.
4447 * PARAMETER(S):
4448 * [I] infoPtr : valid pointer to the listview structure
4449 * [I] INT : column index
4450 * [IO] LPLVCOLUMNW : column information
4451 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4452 * otherwise it is in fact a LPLVCOLUMNA
4454 * RETURN:
4455 * SUCCESS : TRUE
4456 * FAILURE : FALSE
4458 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4460 HDITEMW hdi;
4461 BOOL bResult = FALSE;
4463 if (lpColumn != NULL)
4466 /* initialize memory */
4467 ZeroMemory(&hdi, sizeof(hdi));
4469 if (lpColumn->mask & LVCF_FMT)
4470 hdi.mask |= HDI_FORMAT;
4472 if (lpColumn->mask & LVCF_WIDTH)
4473 hdi.mask |= HDI_WIDTH;
4475 if (lpColumn->mask & LVCF_TEXT)
4477 hdi.mask |= HDI_TEXT;
4478 hdi.cchTextMax = lpColumn->cchTextMax;
4479 hdi.pszText = lpColumn->pszText;
4482 if (lpColumn->mask & LVCF_IMAGE)
4483 hdi.mask |= HDI_IMAGE;
4485 if (lpColumn->mask & LVCF_ORDER)
4486 hdi.mask |= HDI_ORDER;
4488 if (isW)
4489 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4490 else
4491 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4493 if (bResult)
4495 if (lpColumn->mask & LVCF_FMT)
4497 lpColumn->fmt = 0;
4499 if (hdi.fmt & HDF_LEFT)
4500 lpColumn->fmt |= LVCFMT_LEFT;
4501 else if (hdi.fmt & HDF_RIGHT)
4502 lpColumn->fmt |= LVCFMT_RIGHT;
4503 else if (hdi.fmt & HDF_CENTER)
4504 lpColumn->fmt |= LVCFMT_CENTER;
4506 if (hdi.fmt & HDF_IMAGE)
4507 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4509 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4510 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4513 if (lpColumn->mask & LVCF_WIDTH)
4514 lpColumn->cx = hdi.cxy;
4516 if (lpColumn->mask & LVCF_IMAGE)
4517 lpColumn->iImage = hdi.iImage;
4519 if (lpColumn->mask & LVCF_ORDER)
4520 lpColumn->iOrder = hdi.iOrder;
4522 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4523 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4528 return bResult;
4532 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4534 INT i;
4536 if (!lpiArray)
4537 return FALSE;
4539 /* FIXME: little hack */
4540 for (i = 0; i < iCount; i++)
4541 lpiArray[i] = i;
4543 return TRUE;
4546 /***
4547 * DESCRIPTION:
4548 * Retrieves the column width.
4550 * PARAMETER(S):
4551 * [I] infoPtr : valid pointer to the listview structure
4552 * [I] int : column index
4554 * RETURN:
4555 * SUCCESS : column width
4556 * FAILURE : zero
4558 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4560 INT nColumnWidth = 0;
4561 HDITEMW hdi;
4563 TRACE("nColumn=%d\n", nColumn);
4565 switch(LISTVIEW_GetType(infoPtr))
4567 case LVS_LIST:
4568 nColumnWidth = infoPtr->nItemWidth;
4569 break;
4570 case LVS_REPORT:
4571 hdi.mask = HDI_WIDTH;
4572 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4573 nColumnWidth = hdi.cxy;
4574 break;
4575 default:
4576 /* we don't have a 'column' in [SMALL]ICON mode */
4579 TRACE("nColumnWidth=%d\n", nColumnWidth);
4580 return nColumnWidth;
4583 /***
4584 * DESCRIPTION:
4585 * In list or report display mode, retrieves the number of items that can fit
4586 * vertically in the visible area. In icon or small icon display mode,
4587 * retrieves the total number of visible items.
4589 * PARAMETER(S):
4590 * [I] infoPtr : valid pointer to the listview structure
4592 * RETURN:
4593 * Number of fully visible items.
4595 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4597 UINT uView = LISTVIEW_GetType(infoPtr);
4598 INT nItemCount = 0;
4600 if (uView == LVS_LIST)
4602 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4604 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4605 LISTVIEW_GetCountPerColumn(infoPtr);
4608 else if (uView == LVS_REPORT)
4610 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4612 else
4614 nItemCount = GETITEMCOUNT(infoPtr);
4617 return nItemCount;
4621 /***
4622 * DESCRIPTION:
4623 * Retrieves an image list handle.
4625 * PARAMETER(S):
4626 * [I] infoPtr : valid pointer to the listview structure
4627 * [I] nImageList : image list identifier
4629 * RETURN:
4630 * SUCCESS : image list handle
4631 * FAILURE : NULL
4633 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4635 HIMAGELIST himl = NULL;
4637 switch (nImageList)
4639 case LVSIL_NORMAL:
4640 himl = infoPtr->himlNormal;
4641 break;
4642 case LVSIL_SMALL:
4643 himl = infoPtr->himlSmall;
4644 break;
4645 case LVSIL_STATE:
4646 himl = infoPtr->himlState;
4647 break;
4650 return (LRESULT)himl;
4653 /* LISTVIEW_GetISearchString */
4655 /***
4656 * Helper function for LISTVIEW_GetItemT *only*. Tests if an item is selected.
4657 * It is important that no other functions call this because of callbacks.
4659 static inline BOOL is_item_selected(LISTVIEW_INFO *infoPtr, INT nItem)
4661 RANGE selection = { nItem, nItem };
4663 return DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
4664 LISTVIEW_CompareSelectionRanges, 0, DPAS_SORTED) != -1;
4667 /***
4668 * DESCRIPTION:
4669 * Retrieves item attributes.
4671 * PARAMETER(S):
4672 * [I] hwnd : window handle
4673 * [IO] lpLVItem : item info
4674 * [I] internal : if true then we will use tricks that avoid copies
4675 * but are not compatible with the regular interface
4676 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4677 * if FALSE, the lpLVItem is a LPLVITEMA.
4679 * RETURN:
4680 * SUCCESS : TRUE
4681 * FAILURE : FALSE
4683 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
4685 NMLVDISPINFOW dispInfo;
4686 LISTVIEW_ITEM *lpItem;
4687 ITEMHDR* pItemHdr;
4688 HDPA hdpaSubItems;
4690 if (internal && !isW)
4692 ERR("We can't have internal non-Unicode GetItem!\n");
4693 return FALSE;
4696 /* In the following:
4697 * lpLVItem describes the information requested by the user
4698 * lpItem is what we have
4699 * dispInfo is a structure we use to request the missing
4700 * information from the application
4703 TRACE("(lpLVItem=%s, internal=%d, isW=%d)\n", debuglvitem_t(lpLVItem, isW), internal, isW);
4705 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4706 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
4707 return FALSE;
4709 /* a quick optimization if all we're asked is the focus state
4710 * these queries are worth optimising since they are common,
4711 * and can be answered in constant time, without the heavy accesses */
4712 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4713 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4715 lpLVItem->state = 0;
4716 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4717 lpLVItem->state |= LVIS_FOCUSED;
4718 return TRUE;
4721 ZeroMemory(&dispInfo, sizeof(dispInfo));
4723 /* if the app stores all the data, handle it separately */
4724 if (infoPtr->dwStyle & LVS_OWNERDATA)
4726 dispInfo.item.state = 0;
4728 /* if we need to callback, do it now */
4729 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4731 /* NOTE: copy only fields which we _know_ are initialized, some apps
4732 * depend on the uninitialized fields being 0 */
4733 dispInfo.item.mask = lpLVItem->mask;
4734 dispInfo.item.iItem = lpLVItem->iItem;
4735 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4736 if (lpLVItem->mask & LVIF_TEXT)
4738 dispInfo.item.pszText = lpLVItem->pszText;
4739 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4741 if (lpLVItem->mask & LVIF_STATE)
4742 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4743 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4744 *lpLVItem = dispInfo.item;
4745 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4748 /* we store only a little state, so if we're not asked, we're done */
4749 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return FALSE;
4751 /* if focus is handled by us, report it */
4752 if ( !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4754 lpLVItem->state &= ~LVIS_FOCUSED;
4755 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4756 lpLVItem->state |= LVIS_FOCUSED;
4759 /* and do the same for selection, if we handle it */
4760 if ( !(infoPtr->uCallbackMask & LVIS_SELECTED) )
4762 lpLVItem->state &= ~LVIS_SELECTED;
4763 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
4764 is_item_selected(infoPtr, lpLVItem->iItem))
4765 lpLVItem->state |= LVIS_SELECTED;
4768 return TRUE;
4771 /* find the item and subitem structures before we proceed */
4772 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4773 if (hdpaSubItems == NULL) return FALSE;
4775 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4776 return FALSE;
4778 if (lpLVItem->iSubItem)
4780 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4781 if(!lpSubItem) return FALSE;
4782 pItemHdr = &lpSubItem->hdr;
4784 else
4785 pItemHdr = &lpItem->hdr;
4787 /* Do we need to query the state from the app? */
4788 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4790 dispInfo.item.mask |= LVIF_STATE;
4791 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4794 /* Do we need to enquire about the image? */
4795 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4796 dispInfo.item.mask |= LVIF_IMAGE;
4798 /* Do we need to enquire about the text? */
4799 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4801 dispInfo.item.mask |= LVIF_TEXT;
4802 dispInfo.item.pszText = lpLVItem->pszText;
4803 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4804 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4805 *dispInfo.item.pszText = '\0';
4808 /* If we don't have all the requested info, query the application */
4809 if (dispInfo.item.mask != 0)
4811 dispInfo.item.iItem = lpLVItem->iItem;
4812 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4813 dispInfo.item.lParam = lpItem->lParam;
4814 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4815 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4818 /* Now, handle the iImage field */
4819 if (dispInfo.item.mask & LVIF_IMAGE)
4821 lpLVItem->iImage = dispInfo.item.iImage;
4822 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4823 pItemHdr->iImage = dispInfo.item.iImage;
4825 else if (lpLVItem->mask & LVIF_IMAGE)
4826 lpLVItem->iImage = pItemHdr->iImage;
4828 /* The pszText field */
4829 if (dispInfo.item.mask & LVIF_TEXT)
4831 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4832 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4834 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
4835 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
4836 if (lpLVItem->pszText != dispInfo.item.pszText)
4837 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
4839 else if (lpLVItem->mask & LVIF_TEXT)
4841 if (internal) lpLVItem->pszText = pItemHdr->pszText;
4842 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4845 /* if this is a subitem, we're done*/
4846 if (lpLVItem->iSubItem) return TRUE;
4848 /* Next is the lParam field */
4849 if (dispInfo.item.mask & LVIF_PARAM)
4851 lpLVItem->lParam = dispInfo.item.lParam;
4852 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4853 lpItem->lParam = dispInfo.item.lParam;
4855 else if (lpLVItem->mask & LVIF_PARAM)
4856 lpLVItem->lParam = lpItem->lParam;
4858 /* ... the state field (this one is different due to uCallbackmask) */
4859 if (lpLVItem->mask & LVIF_STATE)
4861 lpLVItem->state = lpItem->state;
4862 if (dispInfo.item.mask & LVIF_STATE)
4864 lpLVItem->state &= ~dispInfo.item.stateMask;
4865 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4867 if ( !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4869 lpLVItem->state &= ~LVIS_FOCUSED;
4870 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4871 lpLVItem->state |= LVIS_FOCUSED;
4873 if ( !(infoPtr->uCallbackMask & LVIS_SELECTED) )
4875 lpLVItem->state &= ~LVIS_SELECTED;
4876 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
4877 is_item_selected(infoPtr, lpLVItem->iItem))
4878 lpLVItem->state |= LVIS_SELECTED;
4882 /* and last, but not least, the indent field */
4883 if (lpLVItem->mask & LVIF_INDENT)
4884 lpLVItem->iIndent = lpItem->iIndent;
4886 return TRUE;
4890 /***
4891 * DESCRIPTION:
4892 * Retrieves the position (upper-left) of the listview control item.
4893 * Note that for LVS_ICON style, the upper-left is that of the icon
4894 * and not the bounding box.
4896 * PARAMETER(S):
4897 * [I] infoPtr : valid pointer to the listview structure
4898 * [I] INT : item index
4899 * [O] LPPOINT : coordinate information
4901 * RETURN:
4902 * SUCCESS : TRUE
4903 * FAILURE : FALSE
4905 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4907 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4909 if ((nItem < 0) || (nItem >= GETITEMCOUNT(infoPtr)) || !lpptPosition) return FALSE;
4911 return LISTVIEW_GetItemMeasures(infoPtr, nItem, lpptPosition, NULL, NULL, NULL, NULL);
4914 /***
4915 * Adjust a text rectangle to an integral number of text lines.
4917 static void LISTVIEW_GetIntegralLines(
4918 const LISTVIEW_INFO *infoPtr,
4919 RECT *rcText)
4921 INT i, j, k, l;
4924 * We need to have the bottom to be an intergal number of
4925 * text lines (ntmHeight) below text top that is less than
4926 * or equal to the nItemHeight.
4928 i = infoPtr->nItemHeight - infoPtr->iconSize.cy -
4929 ICON_TOP_PADDING - ICON_BOTTOM_PADDING;
4930 j = i / infoPtr->ntmHeight;
4931 k = j * infoPtr->ntmHeight;
4932 l = rcText->top + k;
4933 rcText->bottom = min(rcText->bottom, l);
4934 rcText->bottom += 1;
4936 TRACE("integral lines, nitemH=%d, ntmH=%d, icon.cy=%ld, i=%d, j=%d, k=%d, rect=(%d,%d)-(%d,%d)\n",
4937 infoPtr->nItemHeight, infoPtr->ntmHeight, infoPtr->iconSize.cy,
4938 i, j, k,
4939 rcText->left, rcText->top, rcText->right, rcText->bottom);
4943 /***
4944 * DESCRIPTION: [INTERNAL]
4945 * Update the bounding rectangle around the text under a large icon.
4946 * This depends on whether it has the focus or not.
4947 * On entry the rectangle's top, left and right should be set.
4948 * On return the bottom will also be set and the width may have been
4949 * modified.
4951 * PARAMETER
4952 * [I] infoPtr : pointer to the listview structure
4953 * [I] nItem : the item for which we are calculating this
4954 * [I/O] rect : the rectangle to be updated
4956 * This appears to be weird, even in the Microsoft implementation.
4958 static BOOL LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *infoPtr, int nItem, RECT *rect)
4960 HDC hdc = GetDC (infoPtr->hwndSelf);
4961 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
4962 UINT uFormat = LISTVIEW_DTFLAGS | DT_CALCRECT;
4963 RECT rcText = *rect;
4964 RECT rcBack = *rect;
4965 BOOL focused, selected;
4966 int dx, dy, old_wid, new_wid;
4967 LVITEMW lvItem;
4969 TRACE("%s, focus item=%d, cur item=%d\n",
4970 (infoPtr->bFocus) ? "Window has focus" : "Window not focused",
4971 infoPtr->nFocusedItem, nItem);
4974 focused = infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED);
4975 selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4977 uFormat |= (focused) ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
4979 /* We (aim to) display the full text. In Windows 95 it appears to
4980 * calculate the size assuming the specified font and then it draws
4981 * the text in that region with the specified font except scaled to
4982 * 10 point (or the height of the system font or ...). Thus if the
4983 * window has 24 point Helvetica the highlit rectangle will be
4984 * taller than the text and if it is 7 point Helvetica then the text
4985 * will be clipped.
4986 * For now we will simply say that it is the correct size to display
4987 * the text in the specified font.
4989 lvItem.mask = LVIF_TEXT;
4990 lvItem.iItem = nItem;
4991 lvItem.iSubItem = 0;
4992 /* We will specify INTERNAL and so will receive back a const
4993 * pointer to the text, rather than specifying a buffer to which
4994 * to copy it. FIXME: what about OWNERDRAW???
4996 if (!LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE)) return FALSE;
4998 InflateRect(&rcText, -2, 0);
4999 DrawTextW (hdc, lvItem.pszText, -1, &rcText, uFormat);
5000 /* Microsoft, in their great wisdom, have decided that the rectangle
5001 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
5002 * not the location. So we have to do the centring ourselves (and take
5003 * responsibility for agreeing off-by-one consistency with them).
5006 old_wid = rcText.right - rcText.left;
5007 new_wid = rcBack.right - rcBack.left;
5008 dx = rcBack.left - rcText.left + (new_wid-old_wid)/2;
5009 dy = rcBack.top - rcText.top;
5010 OffsetRect (&rcText, dx, dy);
5012 if (focused)
5014 rcText.bottom += 2;
5016 else /* not focused, may or may not be selected */
5018 LISTVIEW_GetIntegralLines(infoPtr, &rcText);
5020 *rect = rcText;
5022 TRACE("%s and %s, bounding rect=(%d,%d)-(%d,%d)\n",
5023 (focused) ? "focused(full text)" : "not focused",
5024 (selected) ? "selected" : "not selected",
5025 rect->left, rect->top, rect->right, rect->bottom);
5027 SelectObject (hdc, hOldFont);
5028 ReleaseDC (infoPtr->hwndSelf, hdc);
5030 return TRUE;
5033 /***
5034 * DESCRIPTION:
5035 * Retrieves the bounding rectangle for a listview control item.
5037 * PARAMETER(S):
5038 * [I] infoPtr : valid pointer to the listview structure
5039 * [I] nItem : item index
5040 * [IO] lprc : bounding rectangle coordinates
5041 * lprc->left specifies the portion of the item for which the bounding
5042 * rectangle will be retrieved.
5044 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5045 * including the icon and label.
5047 * * For LVS_ICON
5048 * * Experiment shows that native control returns:
5049 * * width = min (48, length of text line)
5050 * * .left = position.x - (width - iconsize.cx)/2
5051 * * .right = .left + width
5052 * * height = #lines of text * ntmHeight + icon height + 8
5053 * * .top = position.y - 2
5054 * * .bottom = .top + height
5055 * * separation between items .y = itemSpacing.cy - height
5056 * * .x = itemSpacing.cx - width
5057 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5059 * * For LVS_ICON
5060 * * Experiment shows that native control returns:
5061 * * width = iconSize.cx + 16
5062 * * .left = position.x - (width - iconsize.cx)/2
5063 * * .right = .left + width
5064 * * height = iconSize.cy + 4
5065 * * .top = position.y - 2
5066 * * .bottom = .top + height
5067 * * separation between items .y = itemSpacing.cy - height
5068 * * .x = itemSpacing.cx - width
5069 * LVIR_LABEL Returns the bounding rectangle of the item text.
5071 * * For LVS_ICON
5072 * * Experiment shows that native control returns:
5073 * * width = text length
5074 * * .left = position.x - width/2
5075 * * .right = .left + width
5076 * * height = ntmH * linecount + 2
5077 * * .top = position.y + iconSize.cy + 6
5078 * * .bottom = .top + height
5079 * * separation between items .y = itemSpacing.cy - height
5080 * * .x = itemSpacing.cx - width
5081 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5082 * rectangles, but excludes columns in report view.
5084 * RETURN:
5085 * SUCCESS : TRUE
5086 * FAILURE : FALSE
5088 * NOTES
5089 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5090 * upon whether the window has the focus currently and on whether the item
5091 * is the one with the focus. Ensure that the control's record of which
5092 * item has the focus agrees with the items' records.
5094 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5096 RECT label_rect, icon_rect;
5098 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5100 if ((nItem < 0) || (nItem >= GETITEMCOUNT(infoPtr)) || !lprc) return FALSE;
5102 switch(lprc->left)
5104 case LVIR_ICON:
5105 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, lprc, NULL, NULL)) return FALSE;
5106 break;
5108 case LVIR_LABEL:
5109 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, NULL, lprc, NULL)) return FALSE;
5110 break;
5112 case LVIR_BOUNDS:
5113 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, NULL, NULL)) return FALSE;
5114 break;
5116 case LVIR_SELECTBOUNDS:
5117 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, &icon_rect, &label_rect, NULL)) return FALSE;
5118 UnionRect (lprc, &icon_rect, &label_rect);
5119 break;
5121 default:
5122 WARN("Unknown value: %d\n", lprc->left);
5123 return FALSE;
5126 TRACE(" rect=%s\n", debugrect(lprc));
5128 return TRUE;
5132 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem, INT flags, LPRECT lprc)
5134 UINT uView = LISTVIEW_GetType(infoPtr);
5135 INT count;
5137 TRACE("(nItem=%d, nSubItem=%d lprc=%p)\n", nItem, nSubItem,
5138 lprc);
5140 if (!(uView & LVS_REPORT))
5141 return FALSE;
5143 if (flags & LVIR_ICON)
5145 FIXME("Unimplemented LVIR_ICON\n");
5146 return FALSE;
5148 else
5150 int top = min(Header_GetItemCount(infoPtr->hwndHeader), nSubItem - 1);
5152 LISTVIEW_GetItemRect(infoPtr,nItem,lprc);
5153 for (count = 0; count < top; count++)
5154 lprc->left += LISTVIEW_GetColumnWidth(infoPtr,count);
5156 lprc->right = LISTVIEW_GetColumnWidth(infoPtr,(nSubItem-1)) +
5157 lprc->left;
5159 return TRUE;
5163 /***
5164 * DESCRIPTION:
5165 * Retrieves the width of a label.
5167 * PARAMETER(S):
5168 * [I] infoPtr : valid pointer to the listview structure
5170 * RETURN:
5171 * SUCCESS : string width (in pixels)
5172 * FAILURE : zero
5174 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5176 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5177 LVITEMW lvItem;
5179 TRACE("(nItem=%d)\n", nItem);
5181 lvItem.mask = LVIF_TEXT;
5182 lvItem.iItem = nItem;
5183 lvItem.iSubItem = 0;
5184 lvItem.cchTextMax = DISP_TEXT_SIZE;
5185 lvItem.pszText = szDispText;
5186 if (!LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE)) return 0;
5188 /* FIXME: is this right? What if the label is very long? */
5189 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5192 /***
5193 * DESCRIPTION:
5194 * Retrieves the spacing between listview control items.
5196 * PARAMETER(S):
5197 * [I] infoPtr : valid pointer to the listview structure
5198 * [I] BOOL : flag for small or large icon
5200 * RETURN:
5201 * Horizontal + vertical spacing
5203 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5205 LONG lResult;
5207 if (!bSmall)
5209 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5211 else
5213 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
5214 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5215 else
5216 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5218 return lResult;
5221 /***
5222 * DESCRIPTION:
5223 * Retrieves the state of a listview control item.
5225 * PARAMETER(S):
5226 * [I] infoPtr : valid pointer to the listview structure
5227 * [I] nItem : item index
5228 * [I] uMask : state mask
5230 * RETURN:
5231 * State specified by the mask.
5233 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5235 LVITEMW lvItem;
5237 if ((nItem < 0) || (nItem >= GETITEMCOUNT(infoPtr))) return 0;
5239 lvItem.iItem = nItem;
5240 lvItem.iSubItem = 0;
5241 lvItem.mask = LVIF_STATE;
5242 lvItem.stateMask = uMask;
5243 if (!LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE)) return 0;
5245 return lvItem.state & uMask;
5248 /***
5249 * DESCRIPTION:
5250 * Retrieves the text of a listview control item or subitem.
5252 * PARAMETER(S):
5253 * [I] hwnd : window handle
5254 * [I] nItem : item index
5255 * [IO] lpLVItem : item information
5256 * [I] isW : TRUE if lpLVItem is Unicode
5258 * RETURN:
5259 * SUCCESS : string length
5260 * FAILURE : 0
5262 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5264 if (!lpLVItem || (nItem < 0) || (nItem >= GETITEMCOUNT(infoPtr))) return 0;
5266 lpLVItem->mask = LVIF_TEXT;
5267 lpLVItem->iItem = nItem;
5268 if (!LISTVIEW_GetItemT(infoPtr, lpLVItem, FALSE, isW)) return 0;
5270 return textlenT(lpLVItem->pszText, isW);
5273 /***
5274 * DESCRIPTION:
5275 * Searches for an item based on properties + relationships.
5277 * PARAMETER(S):
5278 * [I] infoPtr : valid pointer to the listview structure
5279 * [I] INT : item index
5280 * [I] INT : relationship flag
5282 * RETURN:
5283 * SUCCESS : item index
5284 * FAILURE : -1
5286 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5288 UINT uView = LISTVIEW_GetType(infoPtr);
5289 UINT uMask = 0;
5290 LVFINDINFOW lvFindInfo;
5291 INT nCountPerColumn;
5292 INT i;
5294 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
5296 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5298 if (uFlags & LVNI_CUT)
5299 uMask |= LVIS_CUT;
5301 if (uFlags & LVNI_DROPHILITED)
5302 uMask |= LVIS_DROPHILITED;
5304 if (uFlags & LVNI_FOCUSED)
5305 uMask |= LVIS_FOCUSED;
5307 if (uFlags & LVNI_SELECTED)
5308 uMask |= LVIS_SELECTED;
5310 if (uFlags & LVNI_ABOVE)
5312 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5314 while (nItem >= 0)
5316 nItem--;
5317 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5318 return nItem;
5321 else
5323 lvFindInfo.flags = LVFI_NEARESTXY;
5324 lvFindInfo.vkDirection = VK_UP;
5325 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5326 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5328 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5329 return nItem;
5333 else if (uFlags & LVNI_BELOW)
5335 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5337 while (nItem < GETITEMCOUNT(infoPtr))
5339 nItem++;
5340 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5341 return nItem;
5344 else
5346 lvFindInfo.flags = LVFI_NEARESTXY;
5347 lvFindInfo.vkDirection = VK_DOWN;
5348 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5349 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5351 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5352 return nItem;
5356 else if (uFlags & LVNI_TOLEFT)
5358 if (uView == LVS_LIST)
5360 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5361 while (nItem - nCountPerColumn >= 0)
5363 nItem -= nCountPerColumn;
5364 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5365 return nItem;
5368 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5370 lvFindInfo.flags = LVFI_NEARESTXY;
5371 lvFindInfo.vkDirection = VK_LEFT;
5372 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5373 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5375 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5376 return nItem;
5380 else if (uFlags & LVNI_TORIGHT)
5382 if (uView == LVS_LIST)
5384 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5385 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5387 nItem += nCountPerColumn;
5388 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5389 return nItem;
5392 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5394 lvFindInfo.flags = LVFI_NEARESTXY;
5395 lvFindInfo.vkDirection = VK_RIGHT;
5396 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5397 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5399 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5400 return nItem;
5404 else
5406 nItem++;
5408 /* search by index */
5409 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5411 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5412 return i;
5417 return -1;
5420 /* LISTVIEW_GetNumberOfWorkAreas */
5422 /***
5423 * DESCRIPTION:
5424 * Retrieves the origin coordinates when in icon or small icon display mode.
5426 * PARAMETER(S):
5427 * [I] infoPtr : valid pointer to the listview structure
5428 * [O] lpptOrigin : coordinate information
5430 * RETURN:
5431 * SUCCESS : TRUE
5432 * FAILURE : FALSE
5434 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5436 DWORD lStyle = infoPtr->dwStyle;
5437 UINT uView = lStyle & LVS_TYPEMASK;
5438 INT nHorzPos = 0, nVertPos = 0;
5439 SCROLLINFO scrollInfo;
5441 if (!lpptOrigin) return FALSE;
5443 scrollInfo.cbSize = sizeof(SCROLLINFO);
5444 scrollInfo.fMask = SIF_POS;
5446 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5447 nHorzPos = scrollInfo.nPos;
5448 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5449 nVertPos = scrollInfo.nPos;
5451 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5453 lpptOrigin->x = infoPtr->rcList.left;
5454 lpptOrigin->y = infoPtr->rcList.top;
5455 if (uView == LVS_LIST)
5457 nHorzPos *= LISTVIEW_GetCountPerColumn(infoPtr);
5458 nVertPos = 0;
5460 else if (uView == LVS_REPORT)
5462 nVertPos *= infoPtr->nItemHeight;
5465 lpptOrigin->x -= nHorzPos;
5466 lpptOrigin->y -= nVertPos;
5468 TRACE("(pt=(%ld,%ld))\n", lpptOrigin->x, lpptOrigin->y);
5470 return TRUE;
5473 /***
5474 * DESCRIPTION:
5475 * Retrieves the number of items that are marked as selected.
5477 * PARAMETER(S):
5478 * [I] infoPtr : valid pointer to the listview structure
5480 * RETURN:
5481 * Number of items selected.
5483 static LRESULT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
5485 /* REDO THIS */
5486 INT nSelectedCount = 0;
5487 INT i;
5489 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
5491 if (ListView_GetItemState(infoPtr->hwndSelf, i, LVIS_SELECTED) & LVIS_SELECTED)
5492 nSelectedCount++;
5495 return nSelectedCount;
5498 /***
5499 * DESCRIPTION:
5500 * Retrieves the width of a string.
5502 * PARAMETER(S):
5503 * [I] hwnd : window handle
5504 * [I] lpszText : text string to process
5505 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5507 * RETURN:
5508 * SUCCESS : string width (in pixels)
5509 * FAILURE : zero
5511 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5513 SIZE stringSize;
5515 stringSize.cx = 0;
5516 if (is_textT(lpszText, isW))
5518 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5519 HDC hdc = GetDC(infoPtr->hwndSelf);
5520 HFONT hOldFont = SelectObject(hdc, hFont);
5522 if (isW)
5523 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5524 else
5525 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5526 SelectObject(hdc, hOldFont);
5527 ReleaseDC(infoPtr->hwndSelf, hdc);
5529 return stringSize.cx;
5533 /***
5534 * DESCRIPTION:
5535 * Determines item if a hit or closest if not
5537 * PARAMETER(S):
5538 * [I] infoPtr : valid pointer to the listview structure
5539 * [IO] lpht : hit test information
5540 * [I] subitem : fill out iSubItem.
5541 * [I] bNearItem : return the nearest item
5543 * RETURN:
5544 * SUCCESS : item index of hit
5545 * FAILURE : -1
5547 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL bNearItem)
5549 LONG lStyle = infoPtr->dwStyle;
5550 UINT uView = lStyle & LVS_TYPEMASK;
5551 INT i,j,topindex,bottomindex,nearestItem;
5552 RECT rcItem,rcSubItem;
5553 DWORD xterm, yterm, dist, mindist;
5555 TRACE("(x=%ld, y=%ld)\n", lpht->pt.x, lpht->pt.y);
5557 nearestItem = -1;
5558 mindist = -1;
5560 /* FIXME: get the visible range */
5561 topindex = LISTVIEW_GetTopIndex(infoPtr);
5562 if (uView == LVS_REPORT)
5564 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
5565 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
5567 else
5569 bottomindex = GETITEMCOUNT(infoPtr);
5572 for (i = topindex; i < bottomindex; i++)
5574 rcItem.left = LVIR_BOUNDS;
5575 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5577 if (PtInRect(&rcItem, lpht->pt))
5579 rcSubItem = rcItem;
5580 rcItem.left = LVIR_ICON;
5581 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5583 if (PtInRect(&rcItem, lpht->pt))
5585 lpht->flags = LVHT_ONITEMICON;
5586 lpht->iItem = i;
5587 goto set_subitem;
5591 rcItem.left = LVIR_LABEL;
5592 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5594 if (PtInRect(&rcItem, lpht->pt))
5596 lpht->flags = LVHT_ONITEMLABEL;
5597 lpht->iItem = i;
5598 goto set_subitem;
5602 lpht->flags = LVHT_ONITEMSTATEICON;
5603 lpht->iItem = i;
5604 set_subitem:
5605 if (subitem)
5607 INT nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5608 lpht->iSubItem = 0;
5609 rcSubItem.right = rcSubItem.left;
5610 for (j = 0; j < nColumnCount; j++)
5612 rcSubItem.left = rcSubItem.right;
5613 rcSubItem.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5614 if (PtInRect(&rcSubItem, lpht->pt))
5616 lpht->iSubItem = j;
5617 break;
5621 TRACE("hit on item %d\n", i);
5622 return i;
5624 else if (bNearItem)
5627 * Now compute distance from point to center of boundary
5628 * box. Since we are only interested in the relative
5629 * distance, we can skip the nasty square root operation
5631 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpht->pt.x;
5632 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpht->pt.y;
5633 dist = xterm * xterm + yterm * yterm;
5634 if (mindist < 0 || dist < mindist)
5636 mindist = dist;
5637 nearestItem = i;
5643 lpht->flags = LVHT_NOWHERE;
5645 return bNearItem ? nearestItem : -1;
5649 /***
5650 * DESCRIPTION:
5651 * Determines which listview item is located at the specified position.
5653 * PARAMETER(S):
5654 * [I] infoPtr : valid pointer to the listview structure
5655 * [IO] lpht : hit test information
5656 * [I] subitem : fill out iSubItem.
5658 * RETURN:
5659 * SUCCESS : item index
5660 * FAILURE : -1
5662 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem)
5664 TRACE("(x=%ld, y=%ld)\n", lpht->pt.x, lpht->pt.y);
5666 lpht->flags = 0;
5668 if (infoPtr->rcList.left > lpht->pt.x)
5669 lpht->flags |= LVHT_TOLEFT;
5670 else if (infoPtr->rcList.right < lpht->pt.x)
5671 lpht->flags |= LVHT_TORIGHT;
5673 if (infoPtr->rcList.top > lpht->pt.y)
5674 lpht->flags |= LVHT_ABOVE;
5675 else if (infoPtr->rcList.bottom < lpht->pt.y)
5676 lpht->flags |= LVHT_BELOW;
5678 if (lpht->flags) return -1;
5680 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
5681 * an app might pass only a structure with space up to iItem!
5682 * (MS Office 97 does that for instance in the file open dialog)
5684 return LISTVIEW_SuperHitTestItem(infoPtr, lpht, subitem, FALSE);
5688 /***
5689 * DESCRIPTION:
5690 * Inserts a new column.
5692 * PARAMETER(S):
5693 * [I] infoPtr : valid pointer to the listview structure
5694 * [I] INT : column index
5695 * [I] LPLVCOLUMNW : column information
5697 * RETURN:
5698 * SUCCESS : new column index
5699 * FAILURE : -1
5701 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5702 LPLVCOLUMNW lpColumn, BOOL isW)
5704 RECT rcOld, rcCol;
5705 INT nNewColumn;
5706 HDITEMW hdi;
5708 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5710 if (!lpColumn) return -1;
5712 hdi.mask = hdi.fmt = 0;
5713 if (lpColumn->mask & LVCF_FMT)
5715 /* format member is valid */
5716 hdi.mask |= HDI_FORMAT;
5718 /* set text alignment (leftmost column must be left-aligned) */
5719 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5720 hdi.fmt |= HDF_LEFT;
5721 else if (lpColumn->fmt & LVCFMT_RIGHT)
5722 hdi.fmt |= HDF_RIGHT;
5723 else if (lpColumn->fmt & LVCFMT_CENTER)
5724 hdi.fmt |= HDF_CENTER;
5726 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5727 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5729 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5731 hdi.fmt |= HDF_IMAGE;
5732 hdi.iImage = I_IMAGECALLBACK;
5735 if (lpColumn->fmt & LVCFMT_IMAGE)
5736 ; /* FIXME: enable images for *(sub)items* this column */
5739 if (lpColumn->mask & LVCF_WIDTH)
5741 hdi.mask |= HDI_WIDTH;
5742 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5744 /* make it fill the remainder of the controls width */
5745 HDITEMW hdit;
5746 RECT rcHeader;
5747 INT item_index;
5749 /* get the width of every item except the current one */
5750 hdit.mask = HDI_WIDTH;
5751 hdi.cxy = 0;
5753 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5754 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5755 hdi.cxy += hdit.cxy;
5757 /* retrieve the layout of the header */
5758 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5759 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5761 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5763 else
5764 hdi.cxy = lpColumn->cx;
5767 if (lpColumn->mask & LVCF_TEXT)
5769 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5770 hdi.fmt |= HDF_STRING;
5771 hdi.pszText = lpColumn->pszText;
5772 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5775 if (lpColumn->mask & LVCF_IMAGE)
5777 hdi.mask |= HDI_IMAGE;
5778 hdi.iImage = lpColumn->iImage;
5781 if (lpColumn->mask & LVCF_ORDER)
5783 hdi.mask |= HDI_ORDER;
5784 hdi.iOrder = lpColumn->iOrder;
5787 /* insert item in header control */
5788 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5789 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5790 (WPARAM)nColumn, (LPARAM)&hdi);
5791 if (nNewColumn == -1) return -1;
5792 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5794 /* now we have to actually adjust the data */
5795 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->hdpaItems->nItemCount > 0)
5797 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5798 HDPA hdpaSubItems;
5799 INT nItem, i;
5801 /* preallocate memory, so we can fail gracefully */
5802 if (nNewColumn == 0)
5804 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->hdpaItems->nItemCount);
5805 if (!lpNewItems) return -1;
5806 for (i = 0; i < infoPtr->hdpaItems->nItemCount; i++)
5807 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5808 if (i != infoPtr->hdpaItems->nItemCount)
5810 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5811 COMCTL32_Free(lpNewItems);
5812 return -1;
5816 for (nItem = 0; nItem < infoPtr->hdpaItems->nItemCount; nItem++)
5818 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5819 if (!hdpaSubItems) continue;
5820 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5822 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5823 if (!lpSubItem) break;
5824 if (lpSubItem->iSubItem >= nNewColumn)
5825 lpSubItem->iSubItem++;
5828 /* if we found our subitem, zapp it */
5829 if (nNewColumn == 0)
5831 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5832 lpSubItem = lpNewItems[nItem];
5833 lpSubItem->hdr = lpMainItem->hdr;
5834 lpSubItem->iSubItem = 1;
5835 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5836 lpMainItem->iSubItem = 0;
5837 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5841 COMCTL32_Free(lpNewItems);
5844 /* we don't have to worry abiut display issues in non-report mode */
5845 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5847 /* Need to reset the item width when inserting a new column */
5848 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
5850 LISTVIEW_UpdateScroll(infoPtr);
5852 /* scroll to cover the deleted column, and invalidate for redraw */
5853 rcOld = infoPtr->rcList;
5854 rcOld.left = rcCol.left;
5855 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5856 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5858 return nNewColumn;
5861 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5862 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5863 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5864 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5865 their own sort proc. when sending LVM_SORTITEMS.
5867 /* Platform SDK:
5868 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5870 LVS_SORTXXX must be specified,
5871 LVS_OWNERDRAW is not set,
5872 <item>.pszText is not LPSTR_TEXTCALLBACK.
5874 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5875 are sorted based on item text..."
5877 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5879 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5880 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5881 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5883 /* if we're sorting descending, negate the return value */
5884 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5887 /***
5888 * nESCRIPTION:
5889 * Inserts a new item in the listview control.
5891 * PARAMETER(S):
5892 * [I] infoPtr : valid pointer to the listview structure
5893 * [I] lpLVItem : item information
5894 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5896 * RETURN:
5897 * SUCCESS : new item index
5898 * FAILURE : -1
5900 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5902 LONG lStyle = infoPtr->dwStyle;
5903 UINT uView = lStyle & LVS_TYPEMASK;
5904 INT nItem = -1;
5905 HDPA hdpaSubItems;
5906 NMLISTVIEW nmlv;
5907 LISTVIEW_ITEM *lpItem;
5908 BOOL is_sorted;
5910 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5912 if (lStyle & LVS_OWNERDATA)
5914 nItem = infoPtr->hdpaItems->nItemCount;
5915 infoPtr->hdpaItems->nItemCount++;
5916 return nItem;
5919 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5920 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5922 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5924 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5925 return -1;
5927 /* insert item in listview control data structure */
5928 if ( (hdpaSubItems = DPA_Create(8)) )
5929 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5930 if (nItem == -1) goto fail;
5932 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5933 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5935 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5936 is_sorted ? GETITEMCOUNT( infoPtr ) + 1 : lpLVItem->iItem,
5937 hdpaSubItems );
5938 if (nItem == -1) goto fail;
5940 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5942 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5943 goto fail;
5946 /* if we're sorted, sort the list, and update the index */
5947 if (is_sorted)
5949 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5950 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5951 if (nItem == -1)
5953 ERR("We can't find the item we just inserted, possible memory corruption.");
5954 /* we can't remove it from the list if we can't find it, so just fail */
5955 /* we don't deallocate memory here, as it will probably cause more problems */
5956 return -1;
5960 /* Add the subitem list to the items array. Do this last in case we go to
5961 * fail during the above.
5963 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5965 lpItem->valid = TRUE;
5967 /* send LVN_INSERTITEM notification */
5968 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5969 nmlv.iItem = nItem;
5970 nmlv.lParam = lpItem->lParam;
5971 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5973 /* align items (set position of each item) */
5974 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5976 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
5977 else LISTVIEW_AlignTop(infoPtr);
5980 LISTVIEW_UpdateScroll(infoPtr);
5982 /* FIXME: refresh client area */
5983 LISTVIEW_InvalidateList(infoPtr);
5985 TRACE(" <- %d\n", nItem);
5986 return nItem;
5988 fail:
5989 DPA_DeletePtr(hdpaSubItems, 0);
5990 DPA_Destroy (hdpaSubItems);
5991 COMCTL32_Free (lpItem);
5992 return -1;
5995 /***
5996 * DESCRIPTION:
5997 * Redraws a range of items.
5999 * PARAMETER(S):
6000 * [I] infoPtr : valid pointer to the listview structure
6001 * [I] INT : first item
6002 * [I] INT : last item
6004 * RETURN:
6005 * SUCCESS : TRUE
6006 * FAILURE : FALSE
6008 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6010 INT i;
6012 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6013 max(nFirst, nLast) >= GETITEMCOUNT(infoPtr))
6014 return FALSE;
6016 for (i = nFirst; i <= nLast; i++)
6017 LISTVIEW_InvalidateItem(infoPtr, i);
6019 return TRUE;
6022 /***
6023 * DESCRIPTION:
6024 * Scroll the content of a listview.
6026 * PARAMETER(S):
6027 * [I] infoPtr : valid pointer to the listview structure
6028 * [I] INT : horizontal scroll amount in pixels
6029 * [I] INT : vertical scroll amount in pixels
6031 * RETURN:
6032 * SUCCESS : TRUE
6033 * FAILURE : FALSE
6035 * COMMENTS:
6036 * If the control is in report mode (LVS_REPORT) the control can
6037 * be scrolled only in line increments. "dy" will be rounded to the
6038 * nearest number of pixels that are a whole line. Ex: if line height
6039 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6040 * is passed the the scroll will be 0. (per MSDN 7/2002)
6042 * For: (per experimentaion with native control and CSpy ListView)
6043 * LVS_ICON dy=1 = 1 pixel (vertical only)
6044 * dx ignored
6045 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6046 * dx ignored
6047 * LVS_LIST dx=1 = 1 column (horizontal only)
6048 * but will only scroll 1 column per message
6049 * no matter what the value.
6050 * dy must be 0 or FALSE returned.
6051 * LVS_REPORT dx=1 = 1 pixel
6052 * dy= see above
6055 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6057 switch(LISTVIEW_GetType(infoPtr)) {
6058 case LVS_REPORT:
6059 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6060 dy /= infoPtr->nItemHeight;
6061 break;
6062 case LVS_LIST:
6063 if (dy != 0) return FALSE;
6064 break;
6065 default: /* icon */
6066 dx = 0;
6067 break;
6070 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6071 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6073 return TRUE;
6076 /***
6077 * DESCRIPTION:
6078 * Sets the background color.
6080 * PARAMETER(S):
6081 * [I] infoPtr : valid pointer to the listview structure
6082 * [I] COLORREF : background color
6084 * RETURN:
6085 * SUCCESS : TRUE
6086 * FAILURE : FALSE
6088 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6090 TRACE("(clrBk=%lx)\n", clrBk);
6092 if(infoPtr->clrBk != clrBk) {
6093 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6094 infoPtr->clrBk = clrBk;
6095 if (clrBk == CLR_NONE)
6096 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6097 else
6098 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6099 LISTVIEW_InvalidateList(infoPtr);
6102 return TRUE;
6105 /* LISTVIEW_SetBkImage */
6107 /***
6108 * DESCRIPTION:
6109 * Sets the attributes of a header item.
6111 * PARAMETER(S):
6112 * [I] infoPtr : valid pointer to the listview structure
6113 * [I] INT : column index
6114 * [I] LPLVCOLUMNW : column attributes
6115 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6116 * otherwise it is in fact a LPLVCOLUMNA
6118 * RETURN:
6119 * SUCCESS : TRUE
6120 * FAILURE : FALSE
6122 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6123 LPLVCOLUMNW lpColumn, BOOL isW)
6125 BOOL bResult = FALSE;
6126 HDITEMW hdi, hdiget;
6128 if ((lpColumn != NULL) && (nColumn >= 0) &&
6129 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6131 /* initialize memory */
6132 ZeroMemory(&hdi, sizeof(hdi));
6134 if (lpColumn->mask & LVCF_FMT)
6136 /* format member is valid */
6137 hdi.mask |= HDI_FORMAT;
6139 /* get current format first */
6140 hdiget.mask = HDI_FORMAT;
6141 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6142 /* preserve HDF_STRING if present */
6143 hdi.fmt = hdiget.fmt & HDF_STRING;
6145 /* set text alignment (leftmost column must be left-aligned) */
6146 if (nColumn == 0)
6148 hdi.fmt |= HDF_LEFT;
6150 else
6152 if (lpColumn->fmt & LVCFMT_LEFT)
6153 hdi.fmt |= HDF_LEFT;
6154 else if (lpColumn->fmt & LVCFMT_RIGHT)
6155 hdi.fmt |= HDF_RIGHT;
6156 else if (lpColumn->fmt & LVCFMT_CENTER)
6157 hdi.fmt |= HDF_CENTER;
6160 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6161 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6163 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6164 hdi.fmt |= HDF_IMAGE;
6166 if (lpColumn->fmt & LVCFMT_IMAGE)
6168 hdi.fmt |= HDF_IMAGE;
6169 hdi.iImage = I_IMAGECALLBACK;
6173 if (lpColumn->mask & LVCF_WIDTH)
6175 hdi.mask |= HDI_WIDTH;
6176 hdi.cxy = lpColumn->cx;
6179 if (lpColumn->mask & LVCF_TEXT)
6181 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6182 hdi.pszText = lpColumn->pszText;
6183 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6184 hdi.fmt |= HDF_STRING;
6187 if (lpColumn->mask & LVCF_IMAGE)
6189 hdi.mask |= HDI_IMAGE;
6190 hdi.iImage = lpColumn->iImage;
6193 if (lpColumn->mask & LVCF_ORDER)
6195 hdi.mask |= HDI_ORDER;
6196 hdi.iOrder = lpColumn->iOrder;
6199 /* set header item attributes */
6200 if (isW)
6201 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6202 else
6203 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6206 return bResult;
6209 /***
6210 * DESCRIPTION:
6211 * Sets the column order array
6213 * PARAMETERS:
6214 * [I] infoPtr : valid pointer to the listview structure
6215 * [I] INT : number of elements in column order array
6216 * [I] INT : pointer to column order array
6218 * RETURN:
6219 * SUCCESS : TRUE
6220 * FAILURE : FALSE
6222 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6224 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6226 if (!lpiArray)
6227 return FALSE;
6229 return TRUE;
6233 /***
6234 * DESCRIPTION:
6235 * Sets the width of a column
6237 * PARAMETERS:
6238 * [I] infoPtr : valid pointer to the listview structure
6239 * [I] INT : column index
6240 * [I] INT : column width
6242 * RETURN:
6243 * SUCCESS : TRUE
6244 * FAILURE : FALSE
6246 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6248 HDITEMW hdi;
6249 LRESULT lret;
6250 LONG lStyle = infoPtr->dwStyle;
6251 UINT uView = lStyle & LVS_TYPEMASK;
6252 HDC hdc;
6253 HFONT header_font;
6254 HFONT old_font;
6255 SIZE size;
6256 WCHAR text_buffer[DISP_TEXT_SIZE];
6257 INT header_item_count;
6258 INT item_index;
6259 INT nLabelWidth;
6260 RECT rcHeader;
6261 LVITEMW lvItem;
6262 WCHAR szDispText[DISP_TEXT_SIZE];
6264 if (!infoPtr->hwndHeader) /* make sure we have a header */
6265 return (FALSE);
6267 /* set column width only if in report or list mode */
6268 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6269 return (FALSE);
6271 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6273 /* take care of invalid cx values */
6274 if((uView == LVS_REPORT) && (cx < -2))
6275 cx = LVSCW_AUTOSIZE;
6276 else if (uView == LVS_LIST && (cx < 1))
6277 return FALSE;
6279 /* resize all columns if in LVS_LIST mode */
6280 if(uView == LVS_LIST) {
6281 infoPtr->nItemWidth = cx;
6282 LISTVIEW_InvalidateList(infoPtr);
6283 return TRUE;
6286 /* autosize based on listview items width */
6287 if(cx == LVSCW_AUTOSIZE)
6289 /* set the width of the column to the width of the widest item */
6290 if (iCol == 0 || uView == LVS_LIST)
6292 cx = 0;
6293 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6295 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6296 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6298 if (infoPtr->himlSmall)
6299 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6301 else
6303 lvItem.iSubItem = iCol;
6304 lvItem.mask = LVIF_TEXT;
6305 lvItem.cchTextMax = DISP_TEXT_SIZE;
6306 lvItem.pszText = szDispText;
6307 *lvItem.pszText = '\0';
6308 cx = 0;
6309 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6311 lvItem.iItem = item_index;
6312 if (!LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, TRUE)) continue;
6313 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6314 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6317 cx += TRAILING_PADDING;
6318 } /* autosize based on listview header width */
6319 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6321 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6323 /* if iCol is the last column make it fill the remainder of the controls width */
6324 if(iCol == (header_item_count - 1)) {
6325 /* get the width of every item except the current one */
6326 hdi.mask = HDI_WIDTH;
6327 cx = 0;
6329 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6330 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6331 cx+=hdi.cxy;
6334 /* retrieve the layout of the header */
6335 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6337 cx = (rcHeader.right - rcHeader.left) - cx;
6339 else
6341 /* Despite what the MS docs say, if this is not the last
6342 column, then MS resizes the column to the width of the
6343 largest text string in the column, including headers
6344 and items. This is different from LVSCW_AUTOSIZE in that
6345 LVSCW_AUTOSIZE ignores the header string length.
6348 /* retrieve header font */
6349 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6351 /* retrieve header text */
6352 hdi.mask = HDI_TEXT;
6353 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6354 hdi.pszText = text_buffer;
6356 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6358 /* determine the width of the text in the header */
6359 hdc = GetDC(infoPtr->hwndSelf);
6360 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6362 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6364 SelectObject(hdc, old_font); /* restore the old font */
6365 ReleaseDC(infoPtr->hwndSelf, hdc);
6367 lvItem.iSubItem = iCol;
6368 lvItem.mask = LVIF_TEXT;
6369 lvItem.cchTextMax = DISP_TEXT_SIZE;
6370 lvItem.pszText = szDispText;
6371 *lvItem.pszText = '\0';
6372 cx = size.cx;
6373 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6375 lvItem.iItem = item_index;
6376 if (!LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, TRUE)) continue;
6377 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6378 nLabelWidth += TRAILING_PADDING;
6379 /* While it is possible for subitems to have icons, even MS messes
6380 up the positioning, so I suspect no applications actually use
6381 them. */
6382 if (item_index == 0 && infoPtr->himlSmall)
6383 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6384 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6389 /* call header to update the column change */
6390 hdi.mask = HDI_WIDTH;
6392 hdi.cxy = cx;
6393 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6395 LISTVIEW_InvalidateList(infoPtr);
6397 return lret;
6400 /***
6401 * DESCRIPTION:
6402 * Sets the extended listview style.
6404 * PARAMETERS:
6405 * [I] infoPtr : valid pointer to the listview structure
6406 * [I] DWORD : mask
6407 * [I] DWORD : style
6409 * RETURN:
6410 * SUCCESS : previous style
6411 * FAILURE : 0
6413 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6415 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6417 /* set new style */
6418 if (dwMask)
6419 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6420 else
6421 infoPtr->dwLvExStyle = dwStyle;
6423 return dwOldStyle;
6426 /***
6427 * DESCRIPTION:
6428 * Sets the new hot cursor used during hot tracking and hover selection.
6430 * PARAMETER(S):
6431 * [I] infoPtr : valid pointer to the listview structure
6432 * [I} hCurosr : the new hot cursor handle
6434 * RETURN:
6435 * Returns the previous hot cursor
6437 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6439 HCURSOR oldCursor = infoPtr->hHotCursor;
6440 infoPtr->hHotCursor = hCursor;
6441 return oldCursor;
6445 /***
6446 * DESCRIPTION:
6447 * Sets the hot item index.
6449 * PARAMETERS:
6450 * [I] infoPtr : valid pointer to the listview structure
6451 * [I] INT : index
6453 * RETURN:
6454 * SUCCESS : previous hot item index
6455 * FAILURE : -1 (no hot item)
6457 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6459 INT iOldIndex = infoPtr->nHotItem;
6460 infoPtr->nHotItem = iIndex;
6461 return iOldIndex;
6465 /***
6466 * DESCRIPTION:
6467 * Sets the amount of time the cursor must hover over an item before it is selected.
6469 * PARAMETER(S):
6470 * [I] infoPtr : valid pointer to the listview structure
6471 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6473 * RETURN:
6474 * Returns the previous hover time
6476 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6478 DWORD oldHoverTime = infoPtr->dwHoverTime;
6479 infoPtr->dwHoverTime = dwHoverTime;
6480 return oldHoverTime;
6483 /***
6484 * DESCRIPTION:
6485 * Sets spacing for icons of LVS_ICON style.
6487 * PARAMETER(S):
6488 * [I] infoPtr : valid pointer to the listview structure
6489 * [I] DWORD : MAKELONG(cx, cy)
6491 * RETURN:
6492 * MAKELONG(oldcx, oldcy)
6494 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6496 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6497 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6498 LONG lStyle = infoPtr->dwStyle;
6499 UINT uView = lStyle & LVS_TYPEMASK;
6501 TRACE("requested=(%d,%d)\n", cx, cy);
6503 /* this is supported only for LVS_ICON style */
6504 if (uView != LVS_ICON) return oldspacing;
6506 /* set to defaults, if instructed to */
6507 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6508 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6510 /* if 0 then compute width
6511 * FIXME: Should scan each item and determine max width of
6512 * icon or label, then make that the width */
6513 if (cx == 0)
6514 cx = infoPtr->iconSpacing.cx;
6516 /* if 0 then compute height */
6517 if (cy == 0)
6518 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6519 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6522 infoPtr->iconSpacing.cx = cx;
6523 infoPtr->iconSpacing.cy = cy;
6525 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6526 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6527 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6528 infoPtr->ntmHeight);
6530 /* these depend on the iconSpacing */
6531 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
6532 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6534 return oldspacing;
6537 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6539 INT cx, cy;
6541 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6543 size->cx = cx;
6544 size->cy = cy;
6546 else
6547 size->cx = size->cy = 0;
6550 /***
6551 * DESCRIPTION:
6552 * Sets image lists.
6554 * PARAMETER(S):
6555 * [I] infoPtr : valid pointer to the listview structure
6556 * [I] INT : image list type
6557 * [I] HIMAGELIST : image list handle
6559 * RETURN:
6560 * SUCCESS : old image list
6561 * FAILURE : NULL
6563 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6565 UINT uView = LISTVIEW_GetType(infoPtr);
6566 INT oldHeight = infoPtr->nItemHeight;
6567 HIMAGELIST himlOld = 0;
6569 switch (nType)
6571 case LVSIL_NORMAL:
6572 himlOld = infoPtr->himlNormal;
6573 infoPtr->himlNormal = himl;
6574 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6575 LISTVIEW_SetIconSpacing(infoPtr, 0);
6576 break;
6578 case LVSIL_SMALL:
6579 himlOld = infoPtr->himlSmall;
6580 infoPtr->himlSmall = himl;
6581 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6582 break;
6584 case LVSIL_STATE:
6585 himlOld = infoPtr->himlState;
6586 infoPtr->himlState = himl;
6587 update_icon_size(himl, &infoPtr->iconStateSize);
6588 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6589 break;
6591 default:
6592 ERR("Unknown icon type=%d\n", nType);
6593 return NULL;
6596 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6597 if (infoPtr->nItemHeight != oldHeight)
6598 LISTVIEW_UpdateScroll(infoPtr);
6600 return himlOld;
6603 /***
6604 * DESCRIPTION:
6605 * Preallocates memory (does *not* set the actual count of items !)
6607 * PARAMETER(S):
6608 * [I] infoPtr : valid pointer to the listview structure
6609 * [I] INT : item count (projected number of items to allocate)
6610 * [I] DWORD : update flags
6612 * RETURN:
6613 * SUCCESS : TRUE
6614 * FAILURE : FALSE
6616 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6618 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6620 if (infoPtr->dwStyle & LVS_OWNERDATA)
6622 int precount,topvisible;
6624 TRACE("LVS_OWNERDATA is set!\n");
6625 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6626 FIXME("flags %s %s not implemented\n",
6627 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6628 : "",
6629 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6632 * Internally remove all the selections.
6636 RANGE *selection;
6637 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
6638 if (selection)
6639 LISTVIEW_RemoveSelectionRange(infoPtr,selection->lower,
6640 selection->upper);
6642 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
6644 precount = infoPtr->hdpaItems->nItemCount;
6645 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6646 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6648 /* Grow the hdpaItems array if necessary */
6649 if (nItems > infoPtr->hdpaItems->nMaxCount)
6650 if (!DPA_SetPtr(infoPtr->hdpaItems, nItems - 1, NULL))
6651 return FALSE;
6653 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(infoPtr),
6654 DEFAULT_COLUMN_WIDTH);
6656 LISTVIEW_UpdateSize(infoPtr);
6657 LISTVIEW_UpdateScroll(infoPtr);
6659 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
6660 LISTVIEW_InvalidateList(infoPtr);
6662 else
6664 /* According to MSDN for non-LVS_OWNERDATA this is just
6665 * a performance issue. The control allocates its internal
6666 * data structures for the number of items specified. It
6667 * cuts down on the number of memory allocations. Therefore
6668 * we will just issue a WARN here
6670 WARN("for non-ownerdata performance option not implemented.\n");
6673 return TRUE;
6676 /***
6677 * DESCRIPTION:
6678 * Sets the position of an item.
6680 * PARAMETER(S):
6681 * [I] infoPtr : valid pointer to the listview structure
6682 * [I] INT : item index
6683 * [I] LONG : x coordinate
6684 * [I] LONG : y coordinate
6686 * RETURN:
6687 * SUCCESS : TRUE
6688 * FAILURE : FALSE
6690 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem,
6691 LONG nPosX, LONG nPosY)
6693 UINT lStyle = infoPtr->dwStyle;
6694 UINT uView = lStyle & LVS_TYPEMASK;
6695 LISTVIEW_ITEM *lpItem;
6696 HDPA hdpaSubItems;
6697 BOOL bResult = FALSE;
6699 TRACE("(nItem=%d, X=%ld, Y=%ld)\n", nItem, nPosX, nPosY);
6701 if (lStyle & LVS_OWNERDATA)
6702 return FALSE;
6704 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
6706 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
6708 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
6710 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
6712 POINT orig;
6713 bResult = TRUE;
6714 orig = lpItem->ptPosition;
6715 if ((nPosX == -1) && (nPosY == -1))
6717 /* This point value seems to be an undocumented feature. The
6718 * best guess is that it means either at the origin, or at
6719 * the true beginning of the list. I will assume the origin.
6721 POINT pt1;
6722 if (!LISTVIEW_GetOrigin(infoPtr, &pt1))
6724 pt1.x = 0;
6725 pt1.y = 0;
6727 nPosX = pt1.x;
6728 nPosY = pt1.y;
6729 if (uView == LVS_ICON)
6731 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6732 nPosY += ICON_TOP_PADDING;
6734 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
6735 nPosX, nPosY);
6738 lpItem->ptPosition.x = nPosX;
6739 lpItem->ptPosition.y = nPosY;
6740 if (uView == LVS_ICON)
6742 lpItem->ptPosition.y -= ICON_TOP_PADDING;
6743 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6744 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
6746 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
6747 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
6750 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
6751 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
6754 else
6756 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
6757 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
6765 return bResult;
6768 /***
6769 * DESCRIPTION:
6770 * Sets the state of one or many items.
6772 * PARAMETER(S):
6773 * [I] infoPtr : valid pointer to the listview structure
6774 * [I]INT : item index
6775 * [I] LPLVITEM : item or subitem info
6777 * RETURN:
6778 * SUCCESS : TRUE
6779 * FAILURE : FALSE
6781 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6783 BOOL bResult = TRUE;
6784 LVITEMW lvItem;
6786 TRACE("(nItem=%d, state=%x, stateMask=%x)\n", nItem, lpLVItem->state,
6787 lpLVItem->stateMask);
6789 lvItem.iItem = nItem;
6790 lvItem.iSubItem = 0;
6791 lvItem.mask = LVIF_STATE;
6792 lvItem.state = lpLVItem->state;
6793 lvItem.stateMask = lpLVItem->stateMask;
6795 if (nItem == -1)
6797 /* apply to all items */
6798 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
6799 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6801 else
6802 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6804 return bResult;
6807 /***
6808 * DESCRIPTION:
6809 * Sets the text of an item or subitem.
6811 * PARAMETER(S):
6812 * [I] hwnd : window handle
6813 * [I] nItem : item index
6814 * [I] lpLVItem : item or subitem info
6815 * [I] isW : TRUE if input is Unicode
6817 * RETURN:
6818 * SUCCESS : TRUE
6819 * FAILURE : FALSE
6821 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6823 LVITEMW lvItem;
6825 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(lpLVItem, isW), isW);
6827 if ((nItem < 0) && (nItem >= GETITEMCOUNT(infoPtr))) return FALSE;
6829 lvItem.iItem = nItem;
6830 lvItem.iSubItem = lpLVItem->iSubItem;
6831 lvItem.mask = LVIF_TEXT;
6832 lvItem.pszText = lpLVItem->pszText;
6834 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6837 /***
6838 * DESCRIPTION:
6839 * Set item index that marks the start of a multiple selection.
6841 * PARAMETER(S):
6842 * [I] infoPtr : valid pointer to the listview structure
6843 * [I] INT : index
6845 * RETURN:
6846 * Index number or -1 if there is no selection mark.
6848 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6850 INT nOldIndex = infoPtr->nSelectionMark;
6852 TRACE("(nIndex=%d)\n", nIndex);
6854 infoPtr->nSelectionMark = nIndex;
6856 return nOldIndex;
6859 /***
6860 * DESCRIPTION:
6861 * Sets the text background color.
6863 * PARAMETER(S):
6864 * [I] infoPtr : valid pointer to the listview structure
6865 * [I] COLORREF : text background color
6867 * RETURN:
6868 * SUCCESS : TRUE
6869 * FAILURE : FALSE
6871 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6873 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6875 if (infoPtr->clrTextBk != clrTextBk)
6877 infoPtr->clrTextBk = clrTextBk;
6878 LISTVIEW_InvalidateList(infoPtr);
6881 return TRUE;
6884 /***
6885 * DESCRIPTION:
6886 * Sets the text foreground color.
6888 * PARAMETER(S):
6889 * [I] infoPtr : valid pointer to the listview structure
6890 * [I] COLORREF : text color
6892 * RETURN:
6893 * SUCCESS : TRUE
6894 * FAILURE : FALSE
6896 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6898 TRACE("(clrText=%lx)\n", clrText);
6900 if (infoPtr->clrText != clrText)
6902 infoPtr->clrText = clrText;
6903 LISTVIEW_InvalidateList(infoPtr);
6906 return TRUE;
6909 /* LISTVIEW_SetToolTips */
6910 /* LISTVIEW_SetUnicodeFormat */
6911 /* LISTVIEW_SetWorkAreas */
6913 /***
6914 * DESCRIPTION:
6915 * Callback internally used by LISTVIEW_SortItems()
6917 * PARAMETER(S):
6918 * [I] LPVOID : first LISTVIEW_ITEM to compare
6919 * [I] LPVOID : second LISTVIEW_ITEM to compare
6920 * [I] LPARAM : HWND of control
6922 * RETURN:
6923 * if first comes before second : negative
6924 * if first comes after second : positive
6925 * if first and second are equivalent : zero
6927 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6929 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6930 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6931 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6933 /* Forward the call to the client defined callback */
6934 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6937 /***
6938 * DESCRIPTION:
6939 * Sorts the listview items.
6941 * PARAMETER(S):
6942 * [I] infoPtr : valid pointer to the listview structure
6943 * [I] WPARAM : application-defined value
6944 * [I] LPARAM : pointer to comparision callback
6946 * RETURN:
6947 * SUCCESS : TRUE
6948 * FAILURE : FALSE
6950 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6952 UINT lStyle = infoPtr->dwStyle;
6953 HDPA hdpaSubItems;
6954 LISTVIEW_ITEM *lpItem;
6955 LPVOID selectionMarkItem;
6956 int i;
6958 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6960 if (lStyle & LVS_OWNERDATA) return FALSE;
6962 if (!infoPtr->hdpaItems) return FALSE;
6964 /* if there are 0 or 1 items, there is no need to sort */
6965 if (GETITEMCOUNT(infoPtr) < 2) return TRUE;
6967 if (infoPtr->nFocusedItem >= 0)
6969 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6970 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6971 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6974 infoPtr->pfnCompare = pfnCompare;
6975 infoPtr->lParamSort = lParamSort;
6976 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6978 /* Adjust selections and indices so that they are the way they should
6979 * be after the sort (otherwise, the list items move around, but
6980 * whatever is at the item's previous original position will be
6981 * selected instead)
6983 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6984 for (i=0; i < GETITEMCOUNT(infoPtr); i++)
6986 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6987 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6989 if (lpItem->state & LVIS_SELECTED)
6990 LISTVIEW_AddSelectionRange(infoPtr, i, i);
6991 else
6992 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
6993 if (lpItem->state & LVIS_FOCUSED)
6995 infoPtr->nFocusedItem = i;
6996 lpItem->state &= ~LVIS_FOCUSED;
6999 if (selectionMarkItem != NULL)
7000 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7001 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7003 /* align the items */
7004 LISTVIEW_AlignTop(infoPtr);
7006 /* refresh the display */
7007 LISTVIEW_InvalidateList(infoPtr);
7009 return TRUE;
7012 /***
7013 * DESCRIPTION:
7014 * Updates an items or rearranges the listview control.
7016 * PARAMETER(S):
7017 * [I] infoPtr : valid pointer to the listview structure
7018 * [I] INT : item index
7020 * RETURN:
7021 * SUCCESS : TRUE
7022 * FAILURE : FALSE
7024 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7026 LONG lStyle = infoPtr->dwStyle;
7027 UINT uView = lStyle & LVS_TYPEMASK;
7029 TRACE("(nItem=%d)\n", nItem);
7031 if ((nItem < 0) && (nItem >= GETITEMCOUNT(infoPtr))) return FALSE;
7033 /* rearrange with default alignment style */
7034 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
7035 LISTVIEW_Arrange(infoPtr, 0);
7036 else
7037 LISTVIEW_InvalidateItem(infoPtr, nItem);
7039 return TRUE;
7043 /***
7044 * DESCRIPTION:
7045 * Creates the listview control.
7047 * PARAMETER(S):
7048 * [I] hwnd : window handle
7049 * [I] lpcs : the create parameters
7051 * RETURN:
7052 * Success: 0
7053 * Failure: -1
7055 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7057 LISTVIEW_INFO *infoPtr;
7058 UINT uView = lpcs->style & LVS_TYPEMASK;
7059 LOGFONTW logFont;
7061 TRACE("(lpcs=%p)\n", lpcs);
7063 /* initialize info pointer */
7064 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7065 if (!infoPtr) return -1;
7067 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7069 infoPtr->hwndSelf = hwnd;
7070 infoPtr->dwStyle = lpcs->style;
7071 /* determine the type of structures to use */
7072 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7073 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7075 /* initialize color information */
7076 infoPtr->clrBk = CLR_NONE;
7077 infoPtr->clrText = comctl32_color.clrWindowText;
7078 infoPtr->clrTextBk = CLR_DEFAULT;
7079 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7081 /* set default values */
7082 infoPtr->nFocusedItem = -1;
7083 infoPtr->nSelectionMark = -1;
7084 infoPtr->nHotItem = -1;
7085 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7086 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7087 infoPtr->nEditLabelItem = -1;
7089 /* get default font (icon title) */
7090 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7091 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7092 infoPtr->hFont = infoPtr->hDefaultFont;
7093 LISTVIEW_SaveTextMetrics(infoPtr);
7095 /* create header */
7096 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7097 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7098 0, 0, 0, 0, hwnd, (HMENU)0,
7099 lpcs->hInstance, NULL);
7101 /* set header unicode format */
7102 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
7104 /* set header font */
7105 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7106 (LPARAM)TRUE);
7108 if (uView == LVS_ICON)
7110 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7111 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7113 else if (uView == LVS_REPORT)
7115 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7117 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7119 else
7121 /* set HDS_HIDDEN flag to hide the header bar */
7122 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7123 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7127 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7128 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7130 else
7132 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7133 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7136 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
7137 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
7139 /* display unsupported listview window styles */
7140 LISTVIEW_UnsupportedStyles(lpcs->style);
7142 /* allocate memory for the data structure */
7143 infoPtr->hdpaItems = DPA_Create(10);
7145 /* allocate memory for the selection ranges */
7146 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7148 /* initialize size of items */
7149 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
7150 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7152 /* initialize the hover time to -1(indicating the default system hover time) */
7153 infoPtr->dwHoverTime = -1;
7155 return 0;
7158 /***
7159 * DESCRIPTION:
7160 * Erases the background of the listview control.
7162 * PARAMETER(S):
7163 * [I] infoPtr : valid pointer to the listview structure
7164 * [I] hdc : device context handle
7166 * RETURN:
7167 * SUCCESS : TRUE
7168 * FAILURE : FALSE
7170 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7172 RECT rc;
7174 TRACE("(hdc=%x)\n", hdc);
7176 if (!GetClipBox(hdc, &rc)) return FALSE;
7178 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7182 /***
7183 * DESCRIPTION:
7184 * Helper function for LISTVIEW_[HV]Scroll *only*.
7185 * Performs vertical/horizontal scrolling by a give amount.
7187 * PARAMETER(S):
7188 * [I] infoPtr : valid pointer to the listview structure
7189 * [I] dx : amount of horizontal scroll
7190 * [I] dy : amount of vertical scroll
7192 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7194 /* now we can scroll the list */
7195 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7196 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7197 /* if we have focus, adjust rect */
7198 if (infoPtr->bFocus && !IsRectEmpty(&infoPtr->rcFocus))
7199 OffsetRect(&infoPtr->rcFocus, dx, dy);
7200 UpdateWindow(infoPtr->hwndSelf);
7203 /***
7204 * DESCRIPTION:
7205 * Performs vertical scrolling.
7207 * PARAMETER(S):
7208 * [I] infoPtr : valid pointer to the listview structure
7209 * [I] nScrollCode : scroll code
7210 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7211 * [I] hScrollWnd : scrollbar control window handle
7213 * RETURN:
7214 * Zero
7216 * NOTES:
7217 * SB_LINEUP/SB_LINEDOWN:
7218 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7219 * for LVS_REPORT is 1 line
7220 * for LVS_LIST cannot occur
7223 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7224 INT nScrollDiff, HWND hScrollWnd)
7226 UINT uView = LISTVIEW_GetType(infoPtr);
7227 INT nOldScrollPos, nNewScrollPos;
7228 SCROLLINFO scrollInfo;
7229 BOOL is_an_icon;
7231 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7233 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7235 scrollInfo.cbSize = sizeof(SCROLLINFO);
7236 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7238 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7240 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7242 nOldScrollPos = scrollInfo.nPos;
7243 switch (nScrollCode)
7245 case SB_INTERNAL:
7246 break;
7248 case SB_LINEUP:
7249 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7250 break;
7252 case SB_LINEDOWN:
7253 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7254 break;
7256 case SB_PAGEUP:
7257 nScrollDiff = -scrollInfo.nPage;
7258 break;
7260 case SB_PAGEDOWN:
7261 nScrollDiff = scrollInfo.nPage;
7262 break;
7264 case SB_THUMBPOSITION:
7265 case SB_THUMBTRACK:
7266 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7267 break;
7269 default:
7270 nScrollDiff = 0;
7273 /* quit right away if pos isn't changing */
7274 if (nScrollDiff == 0) return 0;
7276 /* calculate new position, and handle overflows */
7277 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7278 if (nScrollDiff > 0) {
7279 if (nNewScrollPos < nOldScrollPos ||
7280 nNewScrollPos > scrollInfo.nMax)
7281 nNewScrollPos = scrollInfo.nMax;
7282 } else {
7283 if (nNewScrollPos > nOldScrollPos ||
7284 nNewScrollPos < scrollInfo.nMin)
7285 nNewScrollPos = scrollInfo.nMin;
7288 /* set the new position, and reread in case it changed */
7289 scrollInfo.fMask = SIF_POS;
7290 scrollInfo.nPos = nNewScrollPos;
7291 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7293 /* carry on only if it really changed */
7294 if (nNewScrollPos == nOldScrollPos) return 0;
7296 /* now adjust to client coordinates */
7297 nScrollDiff = nOldScrollPos - nNewScrollPos;
7298 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7300 /* and scroll the window */
7301 scroll_list(infoPtr, 0, nScrollDiff);
7303 return 0;
7306 /***
7307 * DESCRIPTION:
7308 * Performs horizontal scrolling.
7310 * PARAMETER(S):
7311 * [I] infoPtr : valid pointer to the listview structure
7312 * [I] nScrollCode : scroll code
7313 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7314 * [I] hScrollWnd : scrollbar control window handle
7316 * RETURN:
7317 * Zero
7319 * NOTES:
7320 * SB_LINELEFT/SB_LINERIGHT:
7321 * for LVS_ICON, LVS_SMALLICON 1 pixel
7322 * for LVS_REPORT is 1 pixel
7323 * for LVS_LIST is 1 column --> which is a 1 because the
7324 * scroll is based on columns not pixels
7327 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7328 INT nScrollDiff, HWND hScrollWnd)
7330 UINT uView = LISTVIEW_GetType(infoPtr);
7331 INT nOldScrollPos, nNewScrollPos;
7332 SCROLLINFO scrollInfo;
7334 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7336 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7338 scrollInfo.cbSize = sizeof(SCROLLINFO);
7339 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7341 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7343 nOldScrollPos = scrollInfo.nPos;
7345 switch (nScrollCode)
7347 case SB_INTERNAL:
7348 break;
7350 case SB_LINELEFT:
7351 nScrollDiff = -1;
7352 break;
7354 case SB_LINERIGHT:
7355 nScrollDiff = 1;
7356 break;
7358 case SB_PAGELEFT:
7359 nScrollDiff = -scrollInfo.nPage;
7360 break;
7362 case SB_PAGERIGHT:
7363 nScrollDiff = scrollInfo.nPage;
7364 break;
7366 case SB_THUMBPOSITION:
7367 case SB_THUMBTRACK:
7368 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7369 break;
7371 default:
7372 nScrollDiff = 0;
7375 /* quit right away if pos isn't changing */
7376 if (nScrollDiff == 0) return 0;
7378 /* calculate new position, and handle overflows */
7379 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7380 if (nScrollDiff > 0) {
7381 if (nNewScrollPos < nOldScrollPos ||
7382 nNewScrollPos > scrollInfo.nMax)
7383 nNewScrollPos = scrollInfo.nMax;
7384 } else {
7385 if (nNewScrollPos > nOldScrollPos ||
7386 nNewScrollPos < scrollInfo.nMin)
7387 nNewScrollPos = scrollInfo.nMin;
7390 /* set the new position, and reread in case it changed */
7391 scrollInfo.fMask = SIF_POS;
7392 scrollInfo.nPos = nNewScrollPos;
7393 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7395 /* carry on only if it really changed */
7396 if (nNewScrollPos == nOldScrollPos) return 0;
7398 if(uView == LVS_REPORT)
7399 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7401 /* now adjust to client coordinates */
7402 nScrollDiff = nOldScrollPos - nNewScrollPos;
7403 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7405 /* and scroll the window */
7406 scroll_list(infoPtr, nScrollDiff, 0);
7408 return 0;
7411 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7413 UINT uView = LISTVIEW_GetType(infoPtr);
7414 INT gcWheelDelta = 0;
7415 UINT pulScrollLines = 3;
7416 SCROLLINFO scrollInfo;
7418 TRACE("(wheelDelta=%d)\n", wheelDelta);
7420 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7421 gcWheelDelta -= wheelDelta;
7423 scrollInfo.cbSize = sizeof(SCROLLINFO);
7424 scrollInfo.fMask = SIF_POS;
7426 switch(uView)
7428 case LVS_ICON:
7429 case LVS_SMALLICON:
7431 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7432 * should be fixed in the future.
7434 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7435 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7436 scrollInfo.nPos + (gcWheelDelta < 0) ?
7437 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7438 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7439 break;
7441 case LVS_REPORT:
7442 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7444 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7446 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7447 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7448 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7451 break;
7453 case LVS_LIST:
7454 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7455 break;
7457 return 0;
7460 /***
7461 * DESCRIPTION:
7462 * ???
7464 * PARAMETER(S):
7465 * [I] infoPtr : valid pointer to the listview structure
7466 * [I] INT : virtual key
7467 * [I] LONG : key data
7469 * RETURN:
7470 * Zero
7472 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7474 UINT uView = LISTVIEW_GetType(infoPtr);
7475 INT nItem = -1;
7476 NMLVKEYDOWN nmKeyDown;
7478 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7480 /* send LVN_KEYDOWN notification */
7481 nmKeyDown.wVKey = nVirtualKey;
7482 nmKeyDown.flags = 0;
7483 notify(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7485 switch (nVirtualKey)
7487 case VK_RETURN:
7488 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7490 notify_return(infoPtr);
7491 notify_itemactivate(infoPtr);
7493 break;
7495 case VK_HOME:
7496 if (GETITEMCOUNT(infoPtr) > 0)
7497 nItem = 0;
7498 break;
7500 case VK_END:
7501 if (GETITEMCOUNT(infoPtr) > 0)
7502 nItem = GETITEMCOUNT(infoPtr) - 1;
7503 break;
7505 case VK_LEFT:
7506 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7507 break;
7509 case VK_UP:
7510 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7511 break;
7513 case VK_RIGHT:
7514 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7515 break;
7517 case VK_DOWN:
7518 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7519 break;
7521 case VK_PRIOR:
7522 if (uView == LVS_REPORT)
7523 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7524 else
7525 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7526 * LISTVIEW_GetCountPerRow(infoPtr);
7527 if(nItem < 0) nItem = 0;
7528 break;
7530 case VK_NEXT:
7531 if (uView == LVS_REPORT)
7532 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7533 else
7534 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7535 * LISTVIEW_GetCountPerRow(infoPtr);
7536 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
7537 break;
7540 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7541 LISTVIEW_KeySelection(infoPtr, nItem);
7543 return 0;
7546 /***
7547 * DESCRIPTION:
7548 * Kills the focus.
7550 * PARAMETER(S):
7551 * [I] infoPtr : valid pointer to the listview structure
7553 * RETURN:
7554 * Zero
7556 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7558 TRACE("()\n");
7560 /* if we did not have the focus, there's nothing to do */
7561 if (!infoPtr->bFocus) return 0;
7563 /* send NM_KILLFOCUS notification */
7564 notify_killfocus(infoPtr);
7566 /* if we have a focus rectagle, get rid of it */
7567 LISTVIEW_ToggleFocusRect(infoPtr);
7569 /* invalidate the selected items before reseting focus flag */
7570 LISTVIEW_InvalidateSelectedItems(infoPtr);
7572 /* set window focus flag */
7573 infoPtr->bFocus = FALSE;
7575 return 0;
7578 /***
7579 * DESCRIPTION:
7580 * Processes double click messages (left mouse button).
7582 * PARAMETER(S):
7583 * [I] infoPtr : valid pointer to the listview structure
7584 * [I] wKey : key flag
7585 * [I] pts : mouse coordinate
7587 * RETURN:
7588 * Zero
7590 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7592 LVHITTESTINFO htInfo;
7593 NMLISTVIEW nmlv;
7595 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7597 htInfo.pt.x = pts.x;
7598 htInfo.pt.y = pts.y;
7600 /* send NM_DBLCLK notification */
7601 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7602 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE);
7603 nmlv.iItem = htInfo.iItem;
7604 nmlv.iSubItem = htInfo.iSubItem;
7605 nmlv.ptAction = htInfo.pt;
7606 notify_listview(infoPtr, NM_DBLCLK, &nmlv);
7608 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7609 if(nmlv.iItem != -1)
7610 notify_itemactivate(infoPtr);
7612 return 0;
7615 /***
7616 * DESCRIPTION:
7617 * Processes mouse down messages (left mouse button).
7619 * PARAMETER(S):
7620 * [I] infoPtr : valid pointer to the listview structure
7621 * [I] wKey : key flag
7622 * [I] pts : mouse coordinate
7624 * RETURN:
7625 * Zero
7627 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7629 LONG lStyle = infoPtr->dwStyle;
7630 static BOOL bGroupSelect = TRUE;
7631 POINT pt = { pts.x, pts.y };
7632 INT nItem;
7634 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7636 /* FIXME: NM_CLICK */
7638 /* send NM_RELEASEDCAPTURE notification */
7639 notify_releasedcapture(infoPtr);
7641 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7643 /* set left button down flag */
7644 infoPtr->bLButtonDown = TRUE;
7646 nItem = LISTVIEW_GetItemAtPt(infoPtr, pt);
7647 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7649 if (lStyle & LVS_SINGLESEL)
7651 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) & LVIS_SELECTED)
7652 && infoPtr->nEditLabelItem == -1)
7653 infoPtr->nEditLabelItem = nItem;
7654 else
7655 LISTVIEW_SetSelection(infoPtr, nItem);
7657 else
7659 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7661 if (bGroupSelect)
7662 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7663 else
7665 LVITEMW item;
7667 item.state = LVIS_SELECTED;
7668 item.stateMask = LVIS_SELECTED;
7670 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7672 LISTVIEW_SetItemFocus(infoPtr, nItem);
7673 infoPtr->nSelectionMark = nItem;
7676 else if (wKey & MK_CONTROL)
7678 LVITEMW item;
7680 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7682 item.state = bGroupSelect ? LVIS_SELECTED : 0;
7683 item.stateMask = LVIS_SELECTED;
7684 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7686 LISTVIEW_SetItemFocus(infoPtr, nItem);
7687 infoPtr->nSelectionMark = nItem;
7689 else if (wKey & MK_SHIFT)
7691 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7693 else
7695 BOOL was_selected =
7696 (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) & LVIS_SELECTED);
7698 /* set selection (clears other pre-existing selections) */
7699 LISTVIEW_SetSelection(infoPtr, nItem);
7701 if (was_selected && infoPtr->nEditLabelItem == -1)
7702 infoPtr->nEditLabelItem = nItem;
7706 else
7708 /* remove all selections */
7709 LISTVIEW_RemoveAllSelections(infoPtr);
7712 return 0;
7715 /***
7716 * DESCRIPTION:
7717 * Processes mouse up messages (left mouse button).
7719 * PARAMETER(S):
7720 * [I] infoPtr : valid pointer to the listview structure
7721 * [I] wKey : key flag
7722 * [I] pts : mouse coordinate
7724 * RETURN:
7725 * Zero
7727 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7729 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7731 if (infoPtr->bLButtonDown)
7733 LVHITTESTINFO lvHitTestInfo;
7734 NMLISTVIEW nmlv;
7736 lvHitTestInfo.pt.x = pts.x;
7737 lvHitTestInfo.pt.y = pts.y;
7739 /* send NM_CLICK notification */
7740 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7741 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE) != -1)
7743 nmlv.iItem = lvHitTestInfo.iItem;
7744 nmlv.iSubItem = lvHitTestInfo.iSubItem;
7746 else
7748 nmlv.iItem = -1;
7749 nmlv.iSubItem = 0;
7751 nmlv.ptAction.x = pts.x;
7752 nmlv.ptAction.y = pts.y;
7753 notify_listview(infoPtr, NM_CLICK, &nmlv);
7755 /* set left button flag */
7756 infoPtr->bLButtonDown = FALSE;
7758 if(infoPtr->nEditLabelItem != -1)
7760 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL) {
7761 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7763 infoPtr->nEditLabelItem = -1;
7767 return 0;
7770 /***
7771 * DESCRIPTION:
7772 * Destroys the listview control (called after WM_DESTROY).
7774 * PARAMETER(S):
7775 * [I] infoPtr : valid pointer to the listview structure
7777 * RETURN:
7778 * Zero
7780 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7782 LONG lStyle = infoPtr->dwStyle;
7784 TRACE("()\n");
7786 /* delete all items */
7787 LISTVIEW_DeleteAllItems(infoPtr);
7789 /* destroy data structure */
7790 DPA_Destroy(infoPtr->hdpaItems);
7791 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7793 /* destroy image lists */
7794 if (!(lStyle & LVS_SHAREIMAGELISTS))
7796 /* FIXME: If the caller does a ImageList_Destroy and then we
7797 * do this code the area will be freed twice. Currently
7798 * this generates an "err:heap:HEAP_ValidateInUseArena
7799 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7800 * has PREV_FREE flag" sometimes.
7802 * We will leak the memory till we figure out how to fix
7804 if (infoPtr->himlNormal)
7805 ImageList_Destroy(infoPtr->himlNormal);
7806 if (infoPtr->himlSmall)
7807 ImageList_Destroy(infoPtr->himlSmall);
7808 if (infoPtr->himlState)
7809 ImageList_Destroy(infoPtr->himlState);
7812 /* destroy font, bkgnd brush */
7813 infoPtr->hFont = 0;
7814 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7815 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7817 /* free listview info pointer*/
7818 COMCTL32_Free(infoPtr);
7820 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7821 return 0;
7824 /***
7825 * DESCRIPTION:
7826 * Handles notifications from children.
7828 * PARAMETER(S):
7829 * [I] infoPtr : valid pointer to the listview structure
7830 * [I] INT : control identifier
7831 * [I] LPNMHDR : notification information
7833 * RETURN:
7834 * Zero
7836 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7838 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7840 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7842 /* handle notification from header control */
7843 if (lpnmh->code == HDN_ENDTRACKW)
7845 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
7846 LISTVIEW_InvalidateList(infoPtr);
7848 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7850 /* Handle sorting by Header Column */
7851 NMLISTVIEW nmlv;
7853 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7854 nmlv.iItem = -1;
7855 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7856 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7858 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7860 /* Idealy this should be done in HDN_ENDTRACKA
7861 * but since SetItemBounds in Header.c is called after
7862 * the notification is sent, it is neccessary to handle the
7863 * update of the scroll bar here (Header.c works fine as it is,
7864 * no need to disturb it)
7866 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
7867 LISTVIEW_UpdateScroll(infoPtr);
7868 LISTVIEW_InvalidateList(infoPtr);
7873 return 0;
7876 /***
7877 * DESCRIPTION:
7878 * Determines the type of structure to use.
7880 * PARAMETER(S):
7881 * [I] infoPtr : valid pointer to the listview structureof the sender
7882 * [I] HWND : listview window handle
7883 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7885 * RETURN:
7886 * Zero
7888 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7890 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7892 if (nCommand == NF_REQUERY)
7893 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7894 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7895 return 0;
7898 /***
7899 * DESCRIPTION:
7900 * Paints/Repaints the listview control.
7902 * PARAMETER(S):
7903 * [I] infoPtr : valid pointer to the listview structure
7904 * [I] HDC : device context handle
7906 * RETURN:
7907 * Zero
7909 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7911 TRACE("(hdc=%x)\n", hdc);
7913 if (hdc)
7914 LISTVIEW_Refresh(infoPtr, hdc);
7915 else
7917 PAINTSTRUCT ps;
7919 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7920 if (!hdc) return 1;
7921 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7922 LISTVIEW_Refresh(infoPtr, hdc);
7923 EndPaint(infoPtr->hwndSelf, &ps);
7926 return 0;
7929 /***
7930 * DESCRIPTION:
7931 * Processes double click messages (right mouse button).
7933 * PARAMETER(S):
7934 * [I] infoPtr : valid pointer to the listview structure
7935 * [I] wKey : key flag
7936 * [I] pts : mouse coordinate
7938 * RETURN:
7939 * Zero
7941 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7943 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7945 /* send NM_RELEASEDCAPTURE notification */
7946 notify_releasedcapture(infoPtr);
7948 /* send NM_RDBLCLK notification */
7949 notify_rdblclk(infoPtr);
7951 return 0;
7954 /***
7955 * DESCRIPTION:
7956 * Processes mouse down messages (right mouse button).
7958 * PARAMETER(S):
7959 * [I] infoPtr : valid pointer to the listview structure
7960 * [I] wKey : key flag
7961 * [I] pts : mouse coordinate
7963 * RETURN:
7964 * Zero
7966 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7968 LVHITTESTINFO lvHitTestInfo;
7969 NMLISTVIEW nmlv;
7970 INT nItem;
7972 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7974 /* FIXME: NM_CLICK */
7976 /* send NM_RELEASEDCAPTURE notification */
7977 notify_releasedcapture(infoPtr);
7979 /* make sure the listview control window has the focus */
7980 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7982 /* set right button down flag */
7983 infoPtr->bRButtonDown = TRUE;
7985 /* determine the index of the selected item */
7986 lvHitTestInfo.pt.x = pts.x;
7987 lvHitTestInfo.pt.y = pts.y;
7988 nItem = LISTVIEW_GetItemAtPt(infoPtr, lvHitTestInfo.pt);
7990 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7992 LISTVIEW_SetItemFocus(infoPtr,nItem);
7993 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7994 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7995 LISTVIEW_SetSelection(infoPtr, nItem);
7997 else
7999 LISTVIEW_RemoveAllSelections(infoPtr);
8003 /* Send NM_RClICK notification */
8004 ZeroMemory(&nmlv, sizeof(nmlv));
8005 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE);
8006 nmlv.iItem = lvHitTestInfo.iItem;
8007 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8008 nmlv.ptAction = lvHitTestInfo.pt;
8009 notify_listview(infoPtr, NM_RCLICK, &nmlv);
8011 return 0;
8014 /***
8015 * DESCRIPTION:
8016 * Processes mouse up messages (right mouse button).
8018 * PARAMETER(S):
8019 * [I] infoPtr : valid pointer to the listview structure
8020 * [I] wKey : key flag
8021 * [I] pts : mouse coordinate
8023 * RETURN:
8024 * Zero
8026 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8028 POINT pt = { pts.x, pts.y };
8030 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8032 if (!infoPtr->bRButtonDown) return 0;
8034 /* set button flag */
8035 infoPtr->bRButtonDown = FALSE;
8037 /* Change to screen coordinate for WM_CONTEXTMENU */
8038 ClientToScreen(infoPtr->hwndSelf, &pt);
8040 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8041 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8042 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8044 return 0;
8048 /***
8049 * DESCRIPTION:
8050 * Sets the cursor.
8052 * PARAMETER(S):
8053 * [I] infoPtr : valid pointer to the listview structure
8054 * [I] hwnd : window handle of window containing the cursor
8055 * [I] nHittest : hit-test code
8056 * [I] wMouseMsg : ideintifier of the mouse message
8058 * RETURN:
8059 * TRUE if cursor is set
8060 * FALSE otherwise
8062 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8064 POINT pt;
8066 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8068 if(!infoPtr->hHotCursor) return FALSE;
8070 GetCursorPos(&pt);
8071 if (LISTVIEW_GetItemAtPt(infoPtr, pt) < 0) return FALSE;
8073 SetCursor(infoPtr->hHotCursor);
8075 return TRUE;
8078 /***
8079 * DESCRIPTION:
8080 * Sets the focus.
8082 * PARAMETER(S):
8083 * [I] infoPtr : valid pointer to the listview structure
8084 * [I] infoPtr : handle of previously focused window
8086 * RETURN:
8087 * Zero
8089 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8091 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
8093 /* if we have the focus already, there's nothing to do */
8094 if (infoPtr->bFocus) return 0;
8096 /* send NM_SETFOCUS notification */
8097 notify_setfocus(infoPtr);
8099 /* put the focus rect back on */
8100 LISTVIEW_ToggleFocusRect(infoPtr);
8102 /* set window focus flag */
8103 infoPtr->bFocus = TRUE;
8105 /* redraw all visible selected items */
8106 LISTVIEW_InvalidateSelectedItems(infoPtr);
8108 return 0;
8111 /***
8112 * DESCRIPTION:
8113 * Sets the font.
8115 * PARAMETER(S):
8116 * [I] infoPtr : valid pointer to the listview structure
8117 * [I] HFONT : font handle
8118 * [I] WORD : redraw flag
8120 * RETURN:
8121 * Zero
8123 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8125 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
8127 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8128 LISTVIEW_SaveTextMetrics(infoPtr);
8130 if (LISTVIEW_GetType(infoPtr) == LVS_REPORT)
8131 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8133 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8135 return 0;
8138 /***
8139 * DESCRIPTION:
8140 * Message handling for WM_SETREDRAW.
8141 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8143 * PARAMETER(S):
8144 * [I] infoPtr : valid pointer to the listview structure
8145 * [I] bRedraw: state of redraw flag
8147 * RETURN:
8148 * DefWinProc return value
8150 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8152 /* FIXME: this is bogus */
8153 LRESULT lResult = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, bRedraw, 0);
8154 if(bRedraw)
8155 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8156 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8157 return lResult;
8160 /***
8161 * DESCRIPTION:
8162 * Resizes the listview control. This function processes WM_SIZE
8163 * messages. At this time, the width and height are not used.
8165 * PARAMETER(S):
8166 * [I] infoPtr : valid pointer to the listview structure
8167 * [I] WORD : new width
8168 * [I] WORD : new height
8170 * RETURN:
8171 * Zero
8173 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8175 LONG lStyle = infoPtr->dwStyle;
8176 UINT uView = lStyle & LVS_TYPEMASK;
8178 TRACE("(width=%d, height=%d)\n", Width, Height);
8180 if (LISTVIEW_UpdateSize(infoPtr))
8182 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8184 if (lStyle & LVS_ALIGNLEFT)
8185 LISTVIEW_AlignLeft(infoPtr);
8186 else
8187 LISTVIEW_AlignTop(infoPtr);
8190 LISTVIEW_UpdateScroll(infoPtr);
8192 /* FIXME: be smarter here */
8193 LISTVIEW_InvalidateList(infoPtr);
8196 return 0;
8199 /***
8200 * DESCRIPTION:
8201 * Sets the size information.
8203 * PARAMETER(S):
8204 * [I] infoPtr : valid pointer to the listview structure
8206 * RETURN:
8207 * Zero if no size change
8208 * 1 of size changed
8210 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8212 LONG lStyle = infoPtr->dwStyle;
8213 UINT uView = lStyle & LVS_TYPEMASK;
8214 RECT rcList;
8215 RECT rcOld;
8217 GetClientRect(infoPtr->hwndSelf, &rcList);
8218 CopyRect(&rcOld,&(infoPtr->rcList));
8219 infoPtr->rcList.left = 0;
8220 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8221 infoPtr->rcList.top = 0;
8222 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8224 if (uView == LVS_LIST)
8226 /* Apparently the "LIST" style is supposed to have the same
8227 * number of items in a column even if there is no scroll bar.
8228 * Since if a scroll bar already exists then the bottom is already
8229 * reduced, only reduce if the scroll bar does not currently exist.
8230 * The "2" is there to mimic the native control. I think it may be
8231 * related to either padding or edges. (GLA 7/2002)
8233 if (!(lStyle & WS_HSCROLL))
8235 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8236 if (infoPtr->rcList.bottom > nHScrollHeight)
8237 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8239 else
8241 if (infoPtr->rcList.bottom > 2)
8242 infoPtr->rcList.bottom -= 2;
8245 else if (uView == LVS_REPORT)
8247 HDLAYOUT hl;
8248 WINDOWPOS wp;
8250 hl.prc = &rcList;
8251 hl.pwpos = &wp;
8252 Header_Layout(infoPtr->hwndHeader, &hl);
8254 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8256 if (!(LVS_NOCOLUMNHEADER & lStyle))
8257 infoPtr->rcList.top = max(wp.cy, 0);
8259 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8262 /***
8263 * DESCRIPTION:
8264 * Processes WM_STYLECHANGED messages.
8266 * PARAMETER(S):
8267 * [I] infoPtr : valid pointer to the listview structure
8268 * [I] WPARAM : window style type (normal or extended)
8269 * [I] LPSTYLESTRUCT : window style information
8271 * RETURN:
8272 * Zero
8274 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8275 LPSTYLESTRUCT lpss)
8277 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8278 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8279 RECT rcList = infoPtr->rcList;
8281 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8282 wStyleType, lpss->styleOld, lpss->styleNew);
8284 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8286 if (wStyleType == GWL_STYLE)
8288 infoPtr->dwStyle = lpss->styleNew;
8290 if (uOldView == LVS_REPORT)
8291 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8293 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8294 ((lpss->styleNew & WS_HSCROLL) == 0))
8295 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8297 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8298 ((lpss->styleNew & WS_VSCROLL) == 0))
8299 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8301 /* If switching modes, then start with no scroll bars and then
8302 * decide.
8304 if (uNewView != uOldView)
8305 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8307 if (uNewView == LVS_ICON)
8309 INT oldcx, oldcy;
8311 /* First readjust the iconSize and if necessary the iconSpacing */
8312 oldcx = infoPtr->iconSize.cx;
8313 oldcy = infoPtr->iconSize.cy;
8314 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8315 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8316 if (infoPtr->himlNormal != NULL)
8318 INT cx, cy;
8319 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8320 infoPtr->iconSize.cx = cx;
8321 infoPtr->iconSize.cy = cy;
8323 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8325 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8326 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8327 LISTVIEW_SetIconSpacing(infoPtr,0);
8330 /* Now update the full item width and height */
8331 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8332 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8333 if (lpss->styleNew & LVS_ALIGNLEFT)
8334 LISTVIEW_AlignLeft(infoPtr);
8335 else
8336 LISTVIEW_AlignTop(infoPtr);
8338 else if (uNewView == LVS_REPORT)
8340 HDLAYOUT hl;
8341 WINDOWPOS wp;
8343 hl.prc = &rcList;
8344 hl.pwpos = &wp;
8345 Header_Layout(infoPtr->hwndHeader, &hl);
8346 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8347 wp.flags);
8348 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8349 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8351 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8352 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8353 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8354 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8356 else if (uNewView == LVS_LIST)
8358 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8359 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8360 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8361 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8363 else
8365 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8366 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8367 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8368 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8369 if (lpss->styleNew & LVS_ALIGNLEFT)
8370 LISTVIEW_AlignLeft(infoPtr);
8371 else
8372 LISTVIEW_AlignTop(infoPtr);
8375 /* update the size of the client area */
8376 LISTVIEW_UpdateSize(infoPtr);
8378 /* add scrollbars if needed */
8379 LISTVIEW_UpdateScroll(infoPtr);
8381 /* invalidate client area + erase background */
8382 LISTVIEW_InvalidateList(infoPtr);
8384 /* print the list of unsupported window styles */
8385 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8388 /* If they change the view and we have an active edit control
8389 we will need to kill the control since the redraw will
8390 misplace the edit control.
8392 if (infoPtr->bEditing &&
8393 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8394 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8396 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8399 return 0;
8402 /***
8403 * DESCRIPTION:
8404 * Window procedure of the listview control.
8407 static LRESULT WINAPI
8408 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8410 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8412 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8414 if (!infoPtr && (uMsg != WM_CREATE))
8415 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8417 if (infoPtr)
8419 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8422 switch (uMsg)
8424 case LVM_APPROXIMATEVIEWRECT:
8425 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8426 LOWORD(lParam), HIWORD(lParam));
8427 case LVM_ARRANGE:
8428 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8430 /* case LVN_CANCELEDITLABEL */
8432 /* case LVM_CREATEDRAGIMAGE: */
8434 case LVM_DELETEALLITEMS:
8435 return LISTVIEW_DeleteAllItems(infoPtr);
8437 case LVM_DELETECOLUMN:
8438 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8440 case LVM_DELETEITEM:
8441 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8443 case LVM_EDITLABELW:
8444 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8446 case LVM_EDITLABELA:
8447 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8449 /* case LVN_ENABLEGROUPVIEW: */
8451 case LVM_ENSUREVISIBLE:
8452 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8454 case LVM_FINDITEMW:
8455 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8457 case LVM_FINDITEMA:
8458 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8460 case LVM_GETBKCOLOR:
8461 return infoPtr->clrBk;
8463 /* case LVM_GETBKIMAGE: */
8465 case LVM_GETCALLBACKMASK:
8466 return infoPtr->uCallbackMask;
8468 case LVM_GETCOLUMNA:
8469 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8471 case LVM_GETCOLUMNW:
8472 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8474 case LVM_GETCOLUMNORDERARRAY:
8475 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8477 case LVM_GETCOLUMNWIDTH:
8478 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8480 case LVM_GETCOUNTPERPAGE:
8481 return LISTVIEW_GetCountPerPage(infoPtr);
8483 case LVM_GETEDITCONTROL:
8484 return (LRESULT)infoPtr->hwndEdit;
8486 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8487 return infoPtr->dwLvExStyle;
8489 case LVM_GETHEADER:
8490 return (LRESULT)infoPtr->hwndHeader;
8492 case LVM_GETHOTCURSOR:
8493 return infoPtr->hHotCursor;
8495 case LVM_GETHOTITEM:
8496 return infoPtr->nHotItem;
8498 case LVM_GETHOVERTIME:
8499 return infoPtr->dwHoverTime;
8501 case LVM_GETIMAGELIST:
8502 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8504 /* case LVN_GETINSERTMARK: */
8506 /* case LVN_GETINSERTMARKCOLOR: */
8508 /* case LVN_GETINSERTMARKRECT: */
8510 case LVM_GETISEARCHSTRINGA:
8511 case LVM_GETISEARCHSTRINGW:
8512 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8513 return FALSE;
8515 case LVM_GETITEMA:
8516 return LISTVIEW_GetItemT(infoPtr, (LPLVITEMW)lParam, FALSE, FALSE);
8518 case LVM_GETITEMW:
8519 return LISTVIEW_GetItemT(infoPtr, (LPLVITEMW)lParam, FALSE, TRUE);
8521 case LVM_GETITEMCOUNT:
8522 return GETITEMCOUNT(infoPtr);
8524 case LVM_GETITEMPOSITION:
8525 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8527 case LVM_GETITEMRECT:
8528 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8530 case LVM_GETITEMSPACING:
8531 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8533 case LVM_GETITEMSTATE:
8534 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8536 case LVM_GETITEMTEXTA:
8537 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8539 case LVM_GETITEMTEXTW:
8540 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8542 case LVM_GETNEXTITEM:
8543 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8545 case LVM_GETNUMBEROFWORKAREAS:
8546 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8547 return 1;
8549 case LVM_GETORIGIN:
8550 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8552 /* case LVN_GETOUTLINECOLOR: */
8554 /* case LVM_GETSELECTEDCOLUMN: */
8556 case LVM_GETSELECTEDCOUNT:
8557 return LISTVIEW_GetSelectedCount(infoPtr);
8559 case LVM_GETSELECTIONMARK:
8560 return infoPtr->nSelectionMark;
8562 case LVM_GETSTRINGWIDTHA:
8563 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8565 case LVM_GETSTRINGWIDTHW:
8566 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8568 case LVM_GETSUBITEMRECT:
8569 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, ((LPRECT)lParam)->top,
8570 ((LPRECT)lParam)->left, (LPRECT)lParam);
8572 case LVM_GETTEXTBKCOLOR:
8573 return infoPtr->clrTextBk;
8575 case LVM_GETTEXTCOLOR:
8576 return infoPtr->clrText;
8578 /* case LVN_GETTILEINFO: */
8580 /* case LVN_GETTILEVIEWINFO: */
8582 case LVM_GETTOOLTIPS:
8583 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8584 return FALSE;
8586 case LVM_GETTOPINDEX:
8587 return LISTVIEW_GetTopIndex(infoPtr);
8589 /*case LVM_GETUNICODEFORMAT:
8590 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8591 return FALSE;*/
8593 case LVM_GETVIEWRECT:
8594 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8596 case LVM_GETWORKAREAS:
8597 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8598 return FALSE;
8600 /* case LVN_HASGROUP: */
8602 case LVM_HITTEST:
8603 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE);
8605 case LVM_INSERTCOLUMNA:
8606 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8608 case LVM_INSERTCOLUMNW:
8609 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8611 /* case LVN_INSERTGROUP: */
8613 /* case LVN_INSERTGROUPSORTED: */
8615 case LVM_INSERTITEMA:
8616 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8618 case LVM_INSERTITEMW:
8619 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8621 /* case LVN_INSERTMARKHITTEST: */
8623 /* case LVN_ISGROUPVIEWENABLED: */
8625 /* case LVN_MAPIDTOINDEX: */
8627 /* case LVN_INEDXTOID: */
8629 /* case LVN_MOVEGROUP: */
8631 /* case LVN_MOVEITEMTOGROUP: */
8633 case LVM_REDRAWITEMS:
8634 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8636 /* case LVN_REMOVEALLGROUPS: */
8638 /* case LVN_REMOVEGROUP: */
8640 case LVM_SCROLL:
8641 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8643 case LVM_SETBKCOLOR:
8644 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8646 /* case LVM_SETBKIMAGE: */
8648 case LVM_SETCALLBACKMASK:
8649 infoPtr->uCallbackMask = (UINT)wParam;
8650 return TRUE;
8652 case LVM_SETCOLUMNA:
8653 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8655 case LVM_SETCOLUMNW:
8656 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8658 case LVM_SETCOLUMNORDERARRAY:
8659 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8661 case LVM_SETCOLUMNWIDTH:
8662 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8664 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8665 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8667 /* case LVN_SETGROUPINFO: */
8669 /* case LVN_SETGROUPMETRICS: */
8671 case LVM_SETHOTCURSOR:
8672 return LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8674 case LVM_SETHOTITEM:
8675 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8677 case LVM_SETHOVERTIME:
8678 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8680 case LVM_SETICONSPACING:
8681 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8683 case LVM_SETIMAGELIST:
8684 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8686 /* case LVN_SETINFOTIP: */
8688 /* case LVN_SETINSERTMARK: */
8690 /* case LVN_SETINSERTMARKCOLOR: */
8692 case LVM_SETITEMA:
8693 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8695 case LVM_SETITEMW:
8696 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8698 case LVM_SETITEMCOUNT:
8699 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8701 case LVM_SETITEMPOSITION:
8702 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (INT)LOWORD(lParam),
8703 (INT)HIWORD(lParam));
8705 case LVM_SETITEMPOSITION32:
8706 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, ((POINT*)lParam)->x,
8707 ((POINT*)lParam)->y);
8709 case LVM_SETITEMSTATE:
8710 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8712 case LVM_SETITEMTEXTA:
8713 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8715 case LVM_SETITEMTEXTW:
8716 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8718 /* case LVN_SETOUTLINECOLOR: */
8720 /* case LVN_SETSELECTEDCOLUMN: */
8722 case LVM_SETSELECTIONMARK:
8723 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8725 case LVM_SETTEXTBKCOLOR:
8726 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8728 case LVM_SETTEXTCOLOR:
8729 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8731 /* case LVN_SETTILEINFO: */
8733 /* case LVN_SETTILEVIEWINFO: */
8735 /* case LVN_SETTILEWIDTH: */
8737 /* case LVM_SETTOOLTIPS: */
8739 /* case LVM_SETUNICODEFORMAT: */
8741 /* case LVN_SETVIEW: */
8743 /* case LVM_SETWORKAREAS: */
8745 /* case LVN_SORTGROUPS: */
8747 case LVM_SORTITEMS:
8748 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8750 case LVM_SUBITEMHITTEST:
8751 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE);
8753 case LVM_UPDATE:
8754 return LISTVIEW_Update(infoPtr, (INT)wParam);
8756 case WM_CHAR:
8757 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8759 case WM_COMMAND:
8760 return LISTVIEW_Command(infoPtr, wParam, lParam);
8762 case WM_CREATE:
8763 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8765 case WM_ERASEBKGND:
8766 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8768 case WM_GETDLGCODE:
8769 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8771 case WM_GETFONT:
8772 return infoPtr->hFont;
8774 case WM_HSCROLL:
8775 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8777 case WM_KEYDOWN:
8778 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8780 case WM_KILLFOCUS:
8781 return LISTVIEW_KillFocus(infoPtr);
8783 case WM_LBUTTONDBLCLK:
8784 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8786 case WM_LBUTTONDOWN:
8787 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8789 case WM_LBUTTONUP:
8790 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8792 case WM_MOUSEMOVE:
8793 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8795 case WM_MOUSEHOVER:
8796 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8798 case WM_NCDESTROY:
8799 return LISTVIEW_NCDestroy(infoPtr);
8801 case WM_NOTIFY:
8802 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8804 case WM_NOTIFYFORMAT:
8805 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8807 case WM_PAINT:
8808 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8810 case WM_RBUTTONDBLCLK:
8811 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8813 case WM_RBUTTONDOWN:
8814 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8816 case WM_RBUTTONUP:
8817 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8819 case WM_SETCURSOR:
8820 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8821 return TRUE;
8822 goto fwd_msg;
8824 case WM_SETFOCUS:
8825 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8827 case WM_SETFONT:
8828 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8830 case WM_SETREDRAW:
8831 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8833 case WM_SIZE:
8834 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8836 case WM_STYLECHANGED:
8837 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8839 case WM_SYSCOLORCHANGE:
8840 COMCTL32_RefreshSysColors();
8841 return 0;
8843 /* case WM_TIMER: */
8845 case WM_VSCROLL:
8846 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8848 case WM_MOUSEWHEEL:
8849 if (wParam & (MK_SHIFT | MK_CONTROL))
8850 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8851 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8853 case WM_WINDOWPOSCHANGED:
8854 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8855 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8856 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8857 LISTVIEW_UpdateSize(infoPtr);
8858 LISTVIEW_UpdateScroll(infoPtr);
8860 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8862 /* case WM_WININICHANGE: */
8864 default:
8865 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8866 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8868 fwd_msg:
8869 /* call default window procedure */
8870 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8873 return 0;
8876 /***
8877 * DESCRIPTION:
8878 * Registers the window class.
8880 * PARAMETER(S):
8881 * None
8883 * RETURN:
8884 * None
8886 void LISTVIEW_Register(void)
8888 WNDCLASSW wndClass;
8890 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8891 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8892 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8893 wndClass.cbClsExtra = 0;
8894 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8895 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8896 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8897 wndClass.lpszClassName = WC_LISTVIEWW;
8898 RegisterClassW(&wndClass);
8901 /***
8902 * DESCRIPTION:
8903 * Unregisters the window class.
8905 * PARAMETER(S):
8906 * None
8908 * RETURN:
8909 * None
8911 void LISTVIEW_Unregister(void)
8913 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8916 /***
8917 * DESCRIPTION:
8918 * Handle any WM_COMMAND messages
8920 * PARAMETER(S):
8922 * RETURN:
8924 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8926 switch (HIWORD(wParam))
8928 case EN_UPDATE:
8931 * Adjust the edit window size
8933 WCHAR buffer[1024];
8934 HDC hdc = GetDC(infoPtr->hwndEdit);
8935 HFONT hFont, hOldFont = 0;
8936 RECT rect;
8937 SIZE sz;
8938 int len;
8940 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8941 GetWindowRect(infoPtr->hwndEdit, &rect);
8943 /* Select font to get the right dimension of the string */
8944 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8945 if(hFont != 0)
8947 hOldFont = SelectObject(hdc, hFont);
8950 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8952 TEXTMETRICW textMetric;
8954 /* Add Extra spacing for the next character */
8955 GetTextMetricsW(hdc, &textMetric);
8956 sz.cx += (textMetric.tmMaxCharWidth * 2);
8958 SetWindowPos (
8959 infoPtr->hwndEdit,
8960 HWND_TOP,
8963 sz.cx,
8964 rect.bottom - rect.top,
8965 SWP_DRAWFRAME|SWP_NOMOVE);
8967 if(hFont != 0)
8968 SelectObject(hdc, hOldFont);
8970 ReleaseDC(infoPtr->hwndSelf, hdc);
8972 break;
8975 default:
8976 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8979 return 0;
8983 /***
8984 * DESCRIPTION:
8985 * Subclassed edit control windproc function
8987 * PARAMETER(S):
8989 * RETURN:
8991 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8992 WPARAM wParam, LPARAM lParam, BOOL isW)
8994 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8995 static BOOL bIgnoreKillFocus = FALSE;
8996 BOOL cancel = FALSE;
8998 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8999 hwnd, uMsg, wParam, lParam, isW);
9001 switch (uMsg)
9003 case WM_GETDLGCODE:
9004 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9006 case WM_KILLFOCUS:
9007 if(bIgnoreKillFocus) return TRUE;
9008 break;
9010 case WM_DESTROY:
9012 WNDPROC editProc = infoPtr->EditWndProc;
9013 infoPtr->EditWndProc = 0;
9014 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9015 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9018 case WM_KEYDOWN:
9019 if (VK_ESCAPE == (INT)wParam)
9021 cancel = TRUE;
9022 break;
9024 else if (VK_RETURN == (INT)wParam)
9025 break;
9027 default:
9028 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9031 if (infoPtr->bEditing)
9033 LPWSTR buffer = NULL;
9035 if (!cancel)
9037 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9039 if (len)
9041 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9043 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9044 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9048 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9049 /* eg. Using a messagebox */
9050 bIgnoreKillFocus = TRUE;
9051 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9053 if (buffer) COMCTL32_Free(buffer);
9055 bIgnoreKillFocus = FALSE;
9058 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9059 return TRUE;
9062 /***
9063 * DESCRIPTION:
9064 * Subclassed edit control windproc function
9066 * PARAMETER(S):
9068 * RETURN:
9070 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9072 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9075 /***
9076 * DESCRIPTION:
9077 * Subclassed edit control windproc function
9079 * PARAMETER(S):
9081 * RETURN:
9083 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9085 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9088 /***
9089 * DESCRIPTION:
9090 * Creates a subclassed edit cotrol
9092 * PARAMETER(S):
9094 * RETURN:
9096 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9097 INT x, INT y, INT width, INT height, BOOL isW)
9099 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9100 HWND hedit;
9101 SIZE sz;
9102 HDC hdc;
9103 HDC hOldFont=0;
9104 TEXTMETRICW textMetric;
9105 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9107 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9109 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9110 hdc = GetDC(infoPtr->hwndSelf);
9112 /* Select the font to get appropriate metric dimensions */
9113 if(infoPtr->hFont != 0)
9114 hOldFont = SelectObject(hdc, infoPtr->hFont);
9116 /*Get String Lenght in pixels */
9117 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9119 /*Add Extra spacing for the next character */
9120 GetTextMetricsW(hdc, &textMetric);
9121 sz.cx += (textMetric.tmMaxCharWidth * 2);
9123 if(infoPtr->hFont != 0)
9124 SelectObject(hdc, hOldFont);
9126 ReleaseDC(infoPtr->hwndSelf, hdc);
9127 if (isW)
9128 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9129 else
9130 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9132 if (!hedit) return 0;
9134 infoPtr->EditWndProc = (WNDPROC)
9135 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9136 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9138 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9140 return hedit;