Fix a bunch of bugs related to scrolling.
[wine/gsoc_dplay.git] / dlls / comctl32 / listview.c
blob25a78143839c4f70ba16a8f1a590f01819cdd8c7
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 * 1. No horizontal scrolling when header is larger than the client area.
29 * 2. Drawing optimizations.
30 * 3. Hot item handling.
32 * Notifications:
33 * LISTVIEW_Notify : most notifications from children (editbox and header)
35 * Data structure:
36 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
38 * Advanced functionality:
39 * LISTVIEW_GetNumberOfWorkAreas : not implemented
40 * LISTVIEW_GetISearchString : not implemented
41 * LISTVIEW_GetBkImage : not implemented
42 * LISTVIEW_SetBkImage : not implemented
43 * LISTVIEW_GetColumnOrderArray : simple hack only
44 * LISTVIEW_SetColumnOrderArray : simple hack only
45 * LISTVIEW_Arrange : empty stub
46 * LISTVIEW_ApproximateViewRect : incomplete
47 * LISTVIEW_Update : not completed
49 * Known differences in message stream from native control (not known if
50 * these differences cause problems):
51 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
52 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
53 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
54 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
55 * does *not* invoke DefWindowProc
56 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
57 * processing for "USEDOUBLECLICKTIME".
61 #include "config.h"
62 #include "wine/port.h"
64 #include <ctype.h>
65 #include <string.h>
66 #include <stdlib.h>
67 #include <stdio.h>
69 #include "winbase.h"
70 #include "winnt.h"
71 #include "heap.h"
72 #include "commctrl.h"
73 #include "comctl32.h"
74 #include "wine/debug.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(listview);
78 /* Some definitions for inline edit control */
80 typedef struct tagLV_INTHIT
82 LVHITTESTINFO ht;
83 DWORD distance; /* distance to closest item */
84 INT iDistItem; /* item number that is closest */
85 } LV_INTHIT, *LPLV_INTHIT;
88 typedef struct tagLISTVIEW_SUBITEM
90 LPWSTR pszText;
91 INT iImage;
92 INT iSubItem;
93 } LISTVIEW_SUBITEM;
95 typedef struct tagLISTVIEW_ITEM
97 LPWSTR pszText;
98 INT iImage;
99 UINT state;
100 LPARAM lParam;
101 INT iIndent;
102 POINT ptPosition;
104 } LISTVIEW_ITEM;
106 typedef struct tagLISTVIEW_SELECTION
108 DWORD lower;
109 DWORD upper;
110 } LISTVIEW_SELECTION;
112 typedef struct tagLISTVIEW_INFO
114 HWND hwndSelf;
115 COLORREF clrBk;
116 COLORREF clrText;
117 COLORREF clrTextBk;
118 HIMAGELIST himlNormal;
119 HIMAGELIST himlSmall;
120 HIMAGELIST himlState;
121 BOOL bLButtonDown;
122 BOOL bRButtonDown;
123 INT nFocusedItem;
124 HDPA hdpaSelectionRanges;
125 INT nItemHeight;
126 INT nItemWidth;
127 INT nSelectionMark;
128 INT nHotItem;
129 SHORT notifyFormat;
130 RECT rcList;
131 RECT rcView;
132 SIZE iconSize;
133 SIZE iconSpacing;
134 UINT uCallbackMask;
135 HWND hwndHeader;
136 HFONT hDefaultFont;
137 HCURSOR hHotCursor;
138 HFONT hFont;
139 INT ntmHeight; /* from GetTextMetrics from above font */
140 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
141 BOOL bFocus;
142 DWORD dwExStyle; /* extended listview style */
143 HDPA hdpaItems;
144 PFNLVCOMPARE pfnCompare;
145 LPARAM lParamSort;
146 HWND hwndEdit;
147 BOOL bEditing;
148 WNDPROC EditWndProc;
149 INT nEditLabelItem;
150 DWORD dwHoverTime;
151 INT nColumnCount; /* the number of columns in this control */
153 DWORD lastKeyPressTimestamp; /* Added */
154 WPARAM charCode; /* Added */
155 INT nSearchParamLength; /* Added */
156 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
157 BOOL bIsDrawing;
158 } LISTVIEW_INFO;
160 DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO, hwndSelf);
163 * constants
166 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
167 #define SB_INTERNAL -1
169 /* maximum size of a label */
170 #define DISP_TEXT_SIZE 512
172 /* padding for items in list and small icon display modes */
173 #define WIDTH_PADDING 12
175 /* padding for items in list, report and small icon display modes */
176 #define HEIGHT_PADDING 1
178 /* offset of items in report display mode */
179 #define REPORT_MARGINX 2
181 /* padding for icon in large icon display mode
182 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
183 * that HITTEST will see.
184 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
185 * ICON_TOP_PADDING - sum of the two above.
186 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
187 * LABEL_VERT_OFFSET - between bottom of text and end of box
189 #define ICON_TOP_PADDING_NOTHITABLE 2
190 #define ICON_TOP_PADDING_HITABLE 2
191 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
192 #define ICON_BOTTOM_PADDING 4
193 #define LABEL_VERT_OFFSET 10
195 /* default label width for items in list and small icon display modes */
196 #define DEFAULT_LABEL_WIDTH 40
198 /* default column width for items in list display mode */
199 #define DEFAULT_COLUMN_WIDTH 128
201 /* Size of "line" scroll for V & H scrolls */
202 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
204 /* Padding betwen image and label */
205 #define IMAGE_PADDING 2
207 /* Padding behind the label */
208 #define TRAILING_PADDING 5
210 /* Border for the icon caption */
211 #define CAPTION_BORDER 2
213 * macros
215 /* retrieve the number of items in the listview */
216 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
218 #define LISTVIEW_DUMP(iP) do { \
219 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
220 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
221 iP->nItemHeight, iP->nItemWidth, GetWindowLongW (iP->hwndSelf, GWL_STYLE)); \
222 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n", \
223 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
224 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle); \
225 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld\n", \
226 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
227 iP->iconSpacing.cx, iP->iconSpacing.cy); \
228 } while(0)
232 * forward declarations
234 static LRESULT LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL, BOOL);
235 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *, LPLV_INTHIT, BOOL);
236 static INT LISTVIEW_HitTestItem(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL);
237 static INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *);
238 static INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *);
239 static VOID LISTVIEW_AlignLeft(LISTVIEW_INFO *);
240 static VOID LISTVIEW_AlignTop(LISTVIEW_INFO *);
241 static VOID LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
242 static VOID LISTVIEW_AddSelection(LISTVIEW_INFO *, INT);
243 static BOOL LISTVIEW_AddSubItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
244 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
245 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *);
246 static BOOL LISTVIEW_GetItemBoundBox(LISTVIEW_INFO *, INT, LPRECT);
247 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
248 static LRESULT LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
249 static LRESULT LISTVIEW_GetSubItemRect(LISTVIEW_INFO *, INT, INT, INT, LPRECT);
250 static INT LISTVIEW_GetItemWidth(LISTVIEW_INFO *);
251 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
252 static LRESULT LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
253 static INT LISTVIEW_CalculateWidth(LISTVIEW_INFO *, INT);
254 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
255 static LRESULT LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
256 static BOOL LISTVIEW_InitItemT(LISTVIEW_INFO *, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
257 static BOOL LISTVIEW_InitSubItemT(LISTVIEW_INFO *, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
258 static INT LISTVIEW_MouseSelection(LISTVIEW_INFO *, POINT);
259 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
260 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
261 static VOID LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
262 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
263 static BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *, INT);
264 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, LONG, LONG);
265 static VOID LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
266 static VOID LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
267 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
268 static BOOL LISTVIEW_SetSubItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
269 static LRESULT LISTVIEW_SetViewRect(LISTVIEW_INFO *, LPRECT);
270 static BOOL LISTVIEW_ToggleSelection(LISTVIEW_INFO *, INT);
271 static VOID LISTVIEW_UnsupportedStyles(LONG);
272 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
273 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
274 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
275 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
276 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *, WPARAM, LPARAM);
277 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
278 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
279 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
280 static BOOL LISTVIEW_IsSelected(LISTVIEW_INFO *, INT);
281 static VOID LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *, INT, INT);
282 static void LISTVIEW_FillBackground(LISTVIEW_INFO *, HDC, LPRECT);
283 static void LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *, int, RECT*);
284 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
285 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
286 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
287 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
288 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
289 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
291 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
292 #define KEY_DELAY 450
294 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
296 static inline BOOL is_textW(LPCWSTR text)
298 return text != NULL && text != LPSTR_TEXTCALLBACKW;
301 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
303 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
304 return is_textW(text);
307 static inline int textlenT(LPCWSTR text, BOOL isW)
309 return !is_textT(text, isW) ? 0 :
310 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
313 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
315 if (isDestW)
316 if (isSrcW) lstrcpynW(dest, src, max);
317 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
318 else
319 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
320 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
323 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
325 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
326 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
329 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
331 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
332 n = min(textlenT(text, isW), n);
333 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
336 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
338 LPWSTR wstr = (LPWSTR)text;
340 TRACE("(text=%s, isW=%d)\n", debugtext_t(text, isW), isW);
341 if (!isW && text)
343 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
344 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
345 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
347 TRACE(" wstr=%s\n", debugstr_w(wstr));
348 return wstr;
351 static inline void textfreeT(LPWSTR wstr, BOOL isW)
353 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
357 * dest is a pointer to a Unicode string
358 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
360 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
362 LPWSTR pszText = textdupTtoW(src, isW);
363 BOOL bResult = TRUE;
364 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
365 bResult = Str_SetPtrW(dest, pszText);
366 textfreeT(pszText, isW);
367 return bResult;
370 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
371 WPARAM wParam, LPARAM lParam, BOOL isW)
373 if (isW)
374 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
375 else
376 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
379 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
381 pnmh->hwndFrom = infoPtr->hwndSelf;
382 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
383 pnmh->code = code;
384 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
385 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
388 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
390 hwnd_notify(infoPtr->hwndSelf, LVN_ITEMACTIVATE);
393 static inline BOOL listview_notify(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
395 return notify(infoPtr, code, (LPNMHDR)plvnm);
398 static int tabNotification[] = {
399 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
400 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
401 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
402 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
403 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
404 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
408 static int get_ansi_notification(INT unicodeNotificationCode)
410 int *pTabNotif = tabNotification;
411 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
412 if (*pTabNotif) return *pTabNotif;
413 ERR("unknown notification %x\n", unicodeNotificationCode);
414 return unicodeNotificationCode;
418 Send notification. depends on dispinfoW having same
419 structure as dispinfoA.
420 infoPtr : listview struct
421 notificationCode : *Unicode* notification code
422 pdi : dispinfo structure (can be unicode or ansi)
423 isW : TRUE if dispinfo is Unicode
425 static BOOL dispinfo_notifyT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
427 BOOL bResult = FALSE;
428 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
429 INT realNotifCode;
430 INT cchTempBufMax = 0, savCchTextMax = 0;
431 LPWSTR pszTempBuf = NULL, savPszText = NULL;
433 TRACE("(code=%x, pdi=%p, isW=%d)\n", notificationCode, pdi, isW);
434 TRACE(" notifyFormat=%s\n",
435 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
436 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
437 if (infoPtr->notifyFormat == NFR_ANSI)
438 realNotifCode = get_ansi_notification(notificationCode);
439 else
440 realNotifCode = notificationCode;
442 if (is_textT(pdi->item.pszText, isW))
444 if (isW && infoPtr->notifyFormat == NFR_ANSI)
445 convertToAnsi = TRUE;
446 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
447 convertToUnicode = TRUE;
450 if (convertToAnsi || convertToUnicode)
452 TRACE(" we have to convert the text to the correct format\n");
453 if (notificationCode != LVN_GETDISPINFOW)
454 { /* length of existing text */
455 cchTempBufMax = convertToUnicode ?
456 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
457 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
459 else
460 cchTempBufMax = pdi->item.cchTextMax;
462 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
463 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
464 if (!pszTempBuf) return FALSE;
465 if (convertToUnicode)
466 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
467 pszTempBuf, cchTempBufMax);
468 else
469 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
470 cchTempBufMax, NULL, NULL);
471 TRACE(" text=%s\n", debugtext_t(pszTempBuf, convertToUnicode));
472 savCchTextMax = pdi->item.cchTextMax;
473 savPszText = pdi->item.pszText;
474 pdi->item.pszText = pszTempBuf;
475 pdi->item.cchTextMax = cchTempBufMax;
478 bResult = notify(infoPtr, realNotifCode, (LPNMHDR)pdi);
480 if (convertToUnicode || convertToAnsi)
481 { /* convert back result */
482 TRACE(" returned text=%s\n", debugtext_t(pdi->item.pszText, convertToUnicode));
483 if (convertToUnicode) /* note : pointer can be changed by app ! */
484 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
485 savCchTextMax, NULL, NULL);
486 else
487 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
488 savPszText, savCchTextMax);
489 pdi->item.pszText = savPszText; /* restores our buffer */
490 pdi->item.cchTextMax = savCchTextMax;
491 HeapFree(GetProcessHeap(), 0, pszTempBuf);
493 return bResult;
496 static inline LRESULT LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL internal)
498 return LISTVIEW_GetItemT(infoPtr, lpLVItem, internal, TRUE);
501 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
503 int res;
505 n = min(min(n, strlenW(s1)), strlenW(s2));
506 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
507 return res ? res - sizeof(WCHAR) : res;
510 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
512 static int index = 0;
513 static char buffers[20][256];
514 char* buf = buffers[index++ % 20];
515 if (lpLVItem == NULL) return "(null)";
516 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
517 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
518 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
519 lpLVItem->state, lpLVItem->stateMask,
520 debugtext_tn(lpLVItem->pszText, isW, 80),
521 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
522 lpLVItem->iIndent);
523 return buf;
526 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
528 static int index = 0;
529 static char buffers[20][256];
530 char* buf = buffers[index++ % 20];
531 if (lpColumn == NULL) return "(null)";
532 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
533 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
534 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
535 lpColumn->mask & LVCF_TEXT ? debugtext_tn(lpColumn->pszText, isW, 80): "",
536 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
537 return buf;
540 static BOOL
541 LISTVIEW_SendCustomDrawNotify (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, HDC hdc,
542 RECT rc)
544 NMLVCUSTOMDRAW nmcdhdr;
545 LPNMCUSTOMDRAW nmcd;
547 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage, hdc);
549 nmcd= & nmcdhdr.nmcd;
550 nmcd->hdr.hwndFrom = infoPtr->hwndSelf;
551 nmcd->hdr.idFrom = GetWindowLongW( infoPtr->hwndSelf, GWL_ID);
552 nmcd->hdr.code = NM_CUSTOMDRAW;
553 nmcd->dwDrawStage= dwDrawStage;
554 nmcd->hdc = hdc;
555 nmcd->rc.left = rc.left;
556 nmcd->rc.right = rc.right;
557 nmcd->rc.bottom = rc.bottom;
558 nmcd->rc.top = rc.top;
559 nmcd->dwItemSpec = 0;
560 nmcd->uItemState = 0;
561 nmcd->lItemlParam= 0;
562 nmcdhdr.clrText = infoPtr->clrText;
563 nmcdhdr.clrTextBk= infoPtr->clrBk;
565 return (BOOL)SendMessageW (GetParent (infoPtr->hwndSelf), WM_NOTIFY,
566 (WPARAM) nmcd->hdr.idFrom, (LPARAM)&nmcdhdr);
569 static BOOL
570 LISTVIEW_SendCustomDrawItemNotify (LISTVIEW_INFO *infoPtr, HDC hdc,
571 UINT iItem, UINT iSubItem,
572 UINT uItemDrawState)
574 NMLVCUSTOMDRAW nmcdhdr;
575 LPNMCUSTOMDRAW nmcd;
576 DWORD dwDrawStage,dwItemSpec;
577 UINT uItemState;
578 INT retval;
579 RECT itemRect;
580 LVITEMW item;
582 ZeroMemory(&item,sizeof(item));
583 item.iItem = iItem;
584 item.mask = LVIF_PARAM;
585 ListView_GetItemW(infoPtr->hwndSelf,&item);
587 dwDrawStage=CDDS_ITEM | uItemDrawState;
588 dwItemSpec=iItem;
589 uItemState=0;
591 if (LISTVIEW_IsSelected(infoPtr,iItem)) uItemState|=CDIS_SELECTED;
592 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
593 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
595 itemRect.left = LVIR_BOUNDS;
596 LISTVIEW_GetItemRect(infoPtr, iItem, &itemRect);
598 nmcd= & nmcdhdr.nmcd;
599 nmcd->hdr.hwndFrom = infoPtr->hwndSelf;
600 nmcd->hdr.idFrom = GetWindowLongW( infoPtr->hwndSelf, GWL_ID);
601 nmcd->hdr.code = NM_CUSTOMDRAW;
602 nmcd->dwDrawStage= dwDrawStage;
603 nmcd->hdc = hdc;
604 nmcd->rc.left = itemRect.left;
605 nmcd->rc.right = itemRect.right;
606 nmcd->rc.bottom = itemRect.bottom;
607 nmcd->rc.top = itemRect.top;
608 nmcd->dwItemSpec = dwItemSpec;
609 nmcd->uItemState = uItemState;
610 nmcd->lItemlParam= item.lParam;
611 nmcdhdr.clrText = infoPtr->clrText;
612 nmcdhdr.clrTextBk= infoPtr->clrBk;
613 nmcdhdr.iSubItem =iSubItem;
615 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
616 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
617 nmcd->uItemState, nmcd->lItemlParam);
619 retval=SendMessageW (GetParent (infoPtr->hwndSelf), WM_NOTIFY,
620 (WPARAM)nmcd->hdr.idFrom, (LPARAM)&nmcdhdr);
622 infoPtr->clrText=nmcdhdr.clrText;
623 infoPtr->clrBk =nmcdhdr.clrTextBk;
624 return (BOOL) retval;
627 static inline int get_listview_type(LISTVIEW_INFO *infoPtr)
629 return GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
632 /*************************************************************************
633 * LISTVIEW_ProcessLetterKeys
635 * Processes keyboard messages generated by pressing the letter keys
636 * on the keyboard.
637 * What this does is perform a case insensitive search from the
638 * current position with the following quirks:
639 * - If two chars or more are pressed in quick succession we search
640 * for the corresponding string (e.g. 'abc').
641 * - If there is a delay we wipe away the current search string and
642 * restart with just that char.
643 * - If the user keeps pressing the same character, whether slowly or
644 * fast, so that the search string is entirely composed of this
645 * character ('aaaaa' for instance), then we search for first item
646 * that starting with that character.
647 * - If the user types the above character in quick succession, then
648 * we must also search for the corresponding string ('aaaaa'), and
649 * go to that string if there is a match.
651 * PARAMETERS
652 * [I] hwnd : handle to the window
653 * [I] charCode : the character code, the actual character
654 * [I] keyData : key data
656 * RETURNS
658 * Zero.
660 * BUGS
662 * - The current implementation has a list of characters it will
663 * accept and it ignores averything else. In particular it will
664 * ignore accentuated characters which seems to match what
665 * Windows does. But I'm not sure it makes sense to follow
666 * Windows there.
667 * - We don't sound a beep when the search fails.
669 * SEE ALSO
671 * TREEVIEW_ProcessLetterKeys
673 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
675 INT nItem;
676 INT nSize;
677 INT endidx,idx;
678 LVITEMW item;
679 WCHAR buffer[MAX_PATH];
680 DWORD timestamp,elapsed;
682 /* simple parameter checking */
683 if (!charCode || !keyData)
684 return 0;
686 if (!infoPtr)
687 return 0;
689 /* only allow the valid WM_CHARs through */
690 if (!isalnum(charCode) &&
691 charCode != '.' && charCode != '`' && charCode != '!' &&
692 charCode != '@' && charCode != '#' && charCode != '$' &&
693 charCode != '%' && charCode != '^' && charCode != '&' &&
694 charCode != '*' && charCode != '(' && charCode != ')' &&
695 charCode != '-' && charCode != '_' && charCode != '+' &&
696 charCode != '=' && charCode != '\\'&& charCode != ']' &&
697 charCode != '}' && charCode != '[' && charCode != '{' &&
698 charCode != '/' && charCode != '?' && charCode != '>' &&
699 charCode != '<' && charCode != ',' && charCode != '~')
700 return 0;
702 nSize=GETITEMCOUNT(infoPtr);
703 /* if there's one item or less, there is no where to go */
704 if (nSize <= 1)
705 return 0;
707 /* compute how much time elapsed since last keypress */
708 timestamp=GetTickCount();
709 if (timestamp > infoPtr->lastKeyPressTimestamp) {
710 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
711 } else {
712 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
715 /* update the search parameters */
716 infoPtr->lastKeyPressTimestamp=timestamp;
717 if (elapsed < KEY_DELAY) {
718 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
719 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
721 if (infoPtr->charCode != charCode) {
722 infoPtr->charCode=charCode=0;
724 } else {
725 infoPtr->charCode=charCode;
726 infoPtr->szSearchParam[0]=charCode;
727 infoPtr->nSearchParamLength=1;
728 /* Redundant with the 1 char string */
729 charCode=0;
732 /* and search from the current position */
733 nItem=-1;
734 if (infoPtr->nFocusedItem >= 0) {
735 endidx=infoPtr->nFocusedItem;
736 idx=endidx;
737 /* if looking for single character match,
738 * then we must always move forward
740 if (infoPtr->nSearchParamLength == 1)
741 idx++;
742 } else {
743 endidx=nSize;
744 idx=0;
746 do {
747 if (idx == nSize) {
748 if (endidx == nSize)
749 break;
750 idx=0;
753 /* get item */
754 ZeroMemory(&item, sizeof(item));
755 item.mask = LVIF_TEXT;
756 item.iItem = idx;
757 item.iSubItem = 0;
758 item.pszText = buffer;
759 item.cchTextMax = COUNTOF(buffer);
760 ListView_GetItemW(infoPtr->hwndSelf, &item);
762 /* check for a match */
763 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
764 nItem=idx;
765 break;
766 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
767 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
768 /* This would work but we must keep looking for a longer match */
769 nItem=idx;
771 idx++;
772 } while (idx != endidx);
774 if (nItem != -1) {
775 if (LISTVIEW_KeySelection(infoPtr, nItem)) {
776 /* refresh client area */
777 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
778 UpdateWindow(infoPtr->hwndSelf);
782 return 0;
785 /*************************************************************************
786 * LISTVIEW_UpdateHeaderSize [Internal]
788 * Function to resize the header control
790 * PARAMS
791 * hwnd [I] handle to a window
792 * nNewScrollPos [I] Scroll Pos to Set
794 * RETURNS
795 * nothing
797 * NOTES
799 static VOID LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
801 RECT winRect;
802 POINT point[2];
804 GetWindowRect(infoPtr->hwndHeader, &winRect);
805 point[0].x = winRect.left;
806 point[0].y = winRect.top;
807 point[1].x = winRect.right;
808 point[1].y = winRect.bottom;
810 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
811 point[0].x = -nNewScrollPos;
812 point[1].x += nNewScrollPos;
814 SetWindowPos(infoPtr->hwndHeader,0,
815 point[0].x,point[0].y,point[1].x,point[1].y,
816 SWP_NOZORDER | SWP_NOACTIVATE);
819 /***
820 * DESCRIPTION:
821 * Update the scrollbars. This functions should be called whenever
822 * the content, size or view changes.
824 * PARAMETER(S):
825 * [I] infoPtr : valid pointer to the listview structure
827 * RETURN:
828 * None
830 static VOID LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
832 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
833 UINT uView = lStyle & LVS_TYPEMASK;
834 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
835 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
836 SCROLLINFO scrollInfo;
838 if (lStyle & LVS_NOSCROLL) return;
840 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
841 scrollInfo.cbSize = sizeof(SCROLLINFO);
843 if (uView == LVS_LIST)
845 /* update horizontal scrollbar */
846 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
847 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
848 INT nNumOfItems = GETITEMCOUNT(infoPtr);
850 TRACE("items=%d, perColumn=%d, perRow=%d\n",
851 nNumOfItems, nCountPerColumn, nCountPerRow);
853 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
854 if((nNumOfItems % nCountPerColumn) == 0)
855 scrollInfo.nMax--;
856 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
857 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
858 scrollInfo.nPage = nCountPerRow;
859 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
860 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
861 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
863 else if (uView == LVS_REPORT)
865 BOOL test;
867 /* update vertical scrollbar */
868 scrollInfo.nMin = 0;
869 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
870 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
871 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
872 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
873 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
874 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
875 scrollInfo.nMax, scrollInfo.nPage, test);
876 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
877 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
879 /* update horizontal scrollbar */
880 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
881 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo) == FALSE
882 || GETITEMCOUNT(infoPtr) == 0)
884 scrollInfo.nPos = 0;
886 scrollInfo.nMin = 0;
887 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
888 scrollInfo.nPage = nListWidth;
889 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
890 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
891 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
892 scrollInfo.nMax, scrollInfo.nPage, test);
893 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
894 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
896 /* Update the Header Control */
897 scrollInfo.fMask = SIF_POS;
898 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
899 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
902 else
904 RECT rcView;
906 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
908 INT nViewWidth = rcView.right - rcView.left;
909 INT nViewHeight = rcView.bottom - rcView.top;
911 /* Update Horizontal Scrollbar */
912 scrollInfo.fMask = SIF_POS;
913 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo) == FALSE
914 || GETITEMCOUNT(infoPtr) == 0)
916 scrollInfo.nPos = 0;
918 scrollInfo.nMax = max(nViewWidth, 0)-1;
919 scrollInfo.nMin = 0;
920 scrollInfo.nPage = nListWidth;
921 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
922 TRACE("LVS_ICON/SMALLICON Horz.\n");
923 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
925 /* Update Vertical Scrollbar */
926 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
927 scrollInfo.fMask = SIF_POS;
928 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo) == FALSE
929 || GETITEMCOUNT(infoPtr) == 0)
931 scrollInfo.nPos = 0;
933 scrollInfo.nMax = max(nViewHeight,0)-1;
934 scrollInfo.nMin = 0;
935 scrollInfo.nPage = nListHeight;
936 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
937 TRACE("LVS_ICON/SMALLICON Vert.\n");
938 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
943 /***
944 * DESCRIPTION:
945 * Prints a message for unsupported window styles.
946 * A kind of TODO list for window styles.
948 * PARAMETER(S):
949 * [I] LONG : window style
951 * RETURN:
952 * None
954 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
956 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
957 FIXME(" LVS_NOSCROLL\n");
959 if (lStyle & LVS_NOLABELWRAP)
960 FIXME(" LVS_NOLABELWRAP\n");
962 if (!(lStyle & LVS_SHAREIMAGELISTS))
963 FIXME(" !LVS_SHAREIMAGELISTS\n");
965 if (lStyle & LVS_SORTASCENDING)
966 FIXME(" LVS_SORTASCENDING\n");
968 if (lStyle & LVS_SORTDESCENDING)
969 FIXME(" LVS_SORTDESCENDING\n");
972 /***
973 * DESCRIPTION:
974 * Aligns the items with the top edge of the window.
976 * PARAMETER(S):
977 * [I] infoPtr : valid pointer to the listview structure
979 * RETURN:
980 * None
982 static VOID LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
984 UINT uView = get_listview_type(infoPtr);
985 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
986 POINT ptItem;
987 RECT rcView;
988 INT i, off_x=0, off_y=0;
990 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
992 /* Since SetItemPosition uses upper-left of icon, and for
993 style=LVS_ICON the icon is not left adjusted, get the offset */
994 if (uView == LVS_ICON)
996 off_y = ICON_TOP_PADDING;
997 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
999 ptItem.x = off_x;
1000 ptItem.y = off_y;
1001 ZeroMemory(&rcView, sizeof(RECT));
1002 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1003 off_x, off_y,
1004 infoPtr->rcList.left, infoPtr->rcList.right);
1006 if (nListWidth > infoPtr->nItemWidth)
1008 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1010 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1012 ptItem.x = off_x;
1013 ptItem.y += infoPtr->nItemHeight;
1016 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1017 ptItem.x += infoPtr->nItemWidth;
1018 rcView.right = max(rcView.right, ptItem.x);
1021 rcView.right -= off_x;
1022 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1024 else
1026 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1028 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1029 ptItem.y += infoPtr->nItemHeight;
1032 rcView.right = infoPtr->nItemWidth;
1033 rcView.bottom = ptItem.y-off_y;
1036 LISTVIEW_SetViewRect(infoPtr, &rcView);
1040 /***
1041 * DESCRIPTION:
1042 * Aligns the items with the left edge of the window.
1044 * PARAMETER(S):
1045 * [I] infoPtr : valid pointer to the listview structure
1047 * RETURN:
1048 * None
1050 static VOID LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1052 UINT uView = get_listview_type(infoPtr);
1053 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1054 POINT ptItem;
1055 RECT rcView;
1056 INT i, off_x=0, off_y=0;
1058 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1060 /* Since SetItemPosition uses upper-left of icon, and for
1061 style=LVS_ICON the icon is not left adjusted, get the offset */
1062 if (uView == LVS_ICON)
1064 off_y = ICON_TOP_PADDING;
1065 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1067 ptItem.x = off_x;
1068 ptItem.y = off_y;
1069 ZeroMemory(&rcView, sizeof(RECT));
1070 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1072 if (nListHeight > infoPtr->nItemHeight)
1074 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1076 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1078 ptItem.y = off_y;
1079 ptItem.x += infoPtr->nItemWidth;
1082 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1083 ptItem.y += infoPtr->nItemHeight;
1084 rcView.bottom = max(rcView.bottom, ptItem.y);
1087 rcView.right = ptItem.x + infoPtr->nItemWidth;
1089 else
1091 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1093 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1094 ptItem.x += infoPtr->nItemWidth;
1097 rcView.bottom = infoPtr->nItemHeight;
1098 rcView.right = ptItem.x;
1101 LISTVIEW_SetViewRect(infoPtr, &rcView);
1105 /***
1106 * DESCRIPTION:
1107 * Set the bounding rectangle of all the items.
1109 * PARAMETER(S):
1110 * [I] infoPtr : valid pointer to the listview structure
1111 * [I] LPRECT : bounding rectangle
1113 * RETURN:
1114 * SUCCESS : TRUE
1115 * FAILURE : FALSE
1117 static LRESULT LISTVIEW_SetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1119 BOOL bResult = FALSE;
1121 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1122 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1124 if (lprcView != NULL)
1126 bResult = TRUE;
1127 infoPtr->rcView.left = lprcView->left;
1128 infoPtr->rcView.top = lprcView->top;
1129 infoPtr->rcView.right = lprcView->right;
1130 infoPtr->rcView.bottom = lprcView->bottom;
1133 return bResult;
1136 /***
1137 * DESCRIPTION:
1138 * Retrieves the bounding rectangle of all the items.
1140 * PARAMETER(S):
1141 * [I] infoPtr : valid pointer to the listview structure
1142 * [O] LPRECT : bounding rectangle
1144 * RETURN:
1145 * SUCCESS : TRUE
1146 * FAILURE : FALSE
1148 static LRESULT LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1150 BOOL bResult = FALSE;
1151 POINT ptOrigin;
1153 TRACE("(lprcView=%p)\n", lprcView);
1155 if (lprcView != NULL)
1157 bResult = LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
1158 if (bResult)
1160 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1161 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1162 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1163 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1166 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1167 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1170 return bResult;
1173 /***
1174 * DESCRIPTION:
1175 * Retrieves the subitem pointer associated with the subitem index.
1177 * PARAMETER(S):
1178 * [I] HDPA : DPA handle for a specific item
1179 * [I] INT : index of subitem
1181 * RETURN:
1182 * SUCCESS : subitem pointer
1183 * FAILURE : NULL
1185 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1186 INT nSubItem)
1188 LISTVIEW_SUBITEM *lpSubItem;
1189 INT i;
1191 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1193 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1194 if (lpSubItem != NULL)
1196 if (lpSubItem->iSubItem == nSubItem)
1198 return lpSubItem;
1203 return NULL;
1206 /***
1207 * DESCRIPTION:
1208 * Calculates the width of an item.
1210 * PARAMETER(S):
1211 * [I] infoPtr : valid pointer to the listview structure
1212 * [I] LONG : window style
1214 * RETURN:
1215 * Returns item width.
1217 static INT LISTVIEW_GetItemWidth(LISTVIEW_INFO *infoPtr)
1219 LONG style = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
1220 UINT uView = style & LVS_TYPEMASK;
1221 INT nHeaderItemCount;
1222 RECT rcHeaderItem;
1223 INT nItemWidth = 0;
1224 INT nLabelWidth;
1225 INT i;
1227 TRACE("()\n");
1229 if (uView == LVS_ICON)
1231 nItemWidth = infoPtr->iconSpacing.cx;
1233 else if (uView == LVS_REPORT)
1235 /* calculate width of header */
1236 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1237 for (i = 0; i < nHeaderItemCount; i++)
1239 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1241 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1245 else /* for LVS_SMALLICON and LVS_LIST */
1247 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1249 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1250 nItemWidth = max(nItemWidth, nLabelWidth);
1253 /* default label size */
1254 if (GETITEMCOUNT(infoPtr) == 0)
1256 nItemWidth = DEFAULT_COLUMN_WIDTH;
1258 else
1260 if (nItemWidth == 0)
1262 nItemWidth = DEFAULT_LABEL_WIDTH;
1264 else
1266 /* add padding */
1267 nItemWidth += WIDTH_PADDING;
1269 if (infoPtr->himlSmall != NULL)
1271 nItemWidth += infoPtr->iconSize.cx;
1274 if (infoPtr->himlState != NULL)
1276 nItemWidth += infoPtr->iconSize.cx;
1278 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1282 if(nItemWidth == 0)
1284 /* nItemWidth Cannot be Zero */
1285 nItemWidth = 1;
1287 return nItemWidth;
1290 /***
1291 * DESCRIPTION:
1292 * Calculates the width of a specific item.
1294 * PARAMETER(S):
1295 * [I] infoPtr : valid pointer to the listview structure
1296 * [I] LPSTR : string
1298 * RETURN:
1299 * Returns the width of an item width a specified string.
1301 static INT LISTVIEW_CalculateWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1303 UINT uView = get_listview_type(infoPtr);
1304 INT nHeaderItemCount;
1305 RECT rcHeaderItem;
1306 INT nItemWidth = 0;
1307 INT i;
1309 TRACE("()\n");
1311 if (uView == LVS_ICON)
1313 nItemWidth = infoPtr->iconSpacing.cx;
1315 else if (uView == LVS_REPORT)
1317 /* calculate width of header */
1318 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1319 for (i = 0; i < nHeaderItemCount; i++)
1321 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1323 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1327 else
1329 /* get width of string */
1330 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1332 /* default label size */
1333 if (GETITEMCOUNT(infoPtr) == 0)
1335 nItemWidth = DEFAULT_COLUMN_WIDTH;
1337 else
1339 if (nItemWidth == 0)
1341 nItemWidth = DEFAULT_LABEL_WIDTH;
1343 else
1345 /* add padding */
1346 nItemWidth += WIDTH_PADDING;
1348 if (infoPtr->himlSmall != NULL)
1350 nItemWidth += infoPtr->iconSize.cx;
1353 if (infoPtr->himlState != NULL)
1355 nItemWidth += infoPtr->iconSize.cx;
1361 return nItemWidth;
1364 /***
1365 * DESCRIPTION:
1366 * Retrieves and saves important text metrics info for the current
1367 * Listview font.
1369 * PARAMETER(S):
1370 * [I] infoPtr : valid pointer to the listview structure
1373 static VOID LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1375 TEXTMETRICW tm;
1376 HDC hdc = GetDC(infoPtr->hwndSelf);
1377 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1378 INT oldHeight, oldACW;
1380 GetTextMetricsW(hdc, &tm);
1382 oldHeight = infoPtr->ntmHeight;
1383 oldACW = infoPtr->ntmAveCharWidth;
1384 infoPtr->ntmHeight = tm.tmHeight;
1385 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1387 SelectObject(hdc, hOldFont);
1388 ReleaseDC(infoPtr->hwndSelf, hdc);
1389 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1390 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1394 /***
1395 * DESCRIPTION:
1396 * Calculates the height of an item.
1398 * PARAMETER(S):
1399 * [I] infoPtr : valid pointer to the listview structure
1401 * RETURN:
1402 * Returns item height.
1404 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1406 UINT uView = get_listview_type(infoPtr);
1407 INT nItemHeight = 0;
1409 if (uView == LVS_ICON)
1411 nItemHeight = infoPtr->iconSpacing.cy;
1413 else
1415 if(infoPtr->himlState || infoPtr->himlSmall)
1416 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1417 else
1418 nItemHeight = infoPtr->ntmHeight;
1421 return nItemHeight;
1425 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1427 LISTVIEW_SELECTION *selection;
1428 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1429 INT i;
1431 TRACE("Selections are:\n");
1432 for (i = 0; i < topSelection; i++)
1434 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1435 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1439 /***
1440 * DESCRIPTION:
1441 * A compare function for selection ranges
1443 *PARAMETER(S)
1444 * [I] LPVOID : Item 1;
1445 * [I] LPVOID : Item 2;
1446 * [I] LPARAM : flags
1448 *RETURNS:
1449 * >0 : if Item 1 > Item 2
1450 * <0 : if Item 2 > Item 1
1451 * 0 : if Item 1 == Item 2
1453 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1454 LPARAM flags)
1456 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1457 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1458 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1459 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1460 int rc=0;
1462 if (u1 < l2)
1463 rc= -1;
1465 if (u2 < l1)
1466 rc= 1;
1468 return rc;
1472 * DESCRIPTION:
1473 * Adds a selection range.
1475 * PARAMETER(S):
1476 * [I] infoPtr : valid pointer to the listview structure
1477 * [I] INT : lower item index
1478 * [I] INT : upper item index
1480 * RETURN:
1481 * None
1483 static VOID LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lItem, INT uItem)
1485 LISTVIEW_SELECTION *selection;
1486 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1487 BOOL lowerzero = FALSE;
1489 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1490 selection->lower = lItem;
1491 selection->upper = uItem;
1493 TRACE("Add range %i - %i\n", lItem, uItem);
1494 if (topSelection)
1496 LISTVIEW_SELECTION *checkselection,*checkselection2;
1497 INT index,mergeindex;
1499 /* find overlapping selections */
1500 /* we want to catch adjacent ranges so expand our range by 1 */
1502 selection->upper++;
1503 if (selection->lower == 0)
1504 lowerzero = TRUE;
1505 else
1506 selection->lower--;
1508 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1509 LISTVIEW_CompareSelectionRanges,
1510 0,0);
1511 selection->upper --;
1512 if (lowerzero)
1513 lowerzero = FALSE;
1514 else
1515 selection->lower ++;
1517 if (index >=0)
1519 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1520 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1521 checkselection->upper);
1523 checkselection->lower = min(selection->lower,checkselection->lower);
1524 checkselection->upper = max(selection->upper,checkselection->upper);
1526 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1527 checkselection->upper);
1529 COMCTL32_Free(selection);
1531 /* merge now common selection ranges in the lower group*/
1534 checkselection->upper ++;
1535 if (checkselection->lower == 0)
1536 lowerzero = TRUE;
1537 else
1538 checkselection->lower --;
1540 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1541 checkselection->upper);
1543 /* not sorted yet */
1544 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1545 LISTVIEW_CompareSelectionRanges, 0,
1548 checkselection->upper --;
1549 if (lowerzero)
1550 lowerzero = FALSE;
1551 else
1552 checkselection->lower ++;
1554 if (mergeindex >=0 && mergeindex != index)
1556 TRACE("Merge with index %i\n",mergeindex);
1557 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1558 mergeindex);
1559 checkselection->lower = min(checkselection->lower,
1560 checkselection2->lower);
1561 checkselection->upper = max(checkselection->upper,
1562 checkselection2->upper);
1563 COMCTL32_Free(checkselection2);
1564 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1565 index --;
1568 while (mergeindex > -1 && mergeindex <index);
1570 /* merge now common selection ranges in the upper group*/
1573 checkselection->upper ++;
1574 if (checkselection->lower == 0)
1575 lowerzero = TRUE;
1576 else
1577 checkselection->lower --;
1579 TRACE("search upper range %i (%lu - %lu)\n",index,
1580 checkselection->lower, checkselection->upper);
1582 /* not sorted yet */
1583 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1584 index+1,
1585 LISTVIEW_CompareSelectionRanges, 0,
1588 checkselection->upper --;
1589 if (lowerzero)
1590 lowerzero = FALSE;
1591 else
1592 checkselection->lower ++;
1594 if (mergeindex >=0 && mergeindex !=index)
1596 TRACE("Merge with index %i\n",mergeindex);
1597 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1598 mergeindex);
1599 checkselection->lower = min(checkselection->lower,
1600 checkselection2->lower);
1601 checkselection->upper = max(checkselection->upper,
1602 checkselection2->upper);
1603 COMCTL32_Free(checkselection2);
1604 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1607 while (mergeindex > -1);
1609 else
1612 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1613 LISTVIEW_CompareSelectionRanges, 0,
1614 DPAS_INSERTAFTER);
1616 TRACE("Insert before index %i\n",index);
1617 if (index == -1)
1618 index = 0;
1619 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1622 else
1624 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1627 * Incase of error
1629 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1630 LISTVIEW_PrintSelectionRanges(infoPtr);
1634 * DESCRIPTION:
1635 * check if a specified index is selected.
1637 * PARAMETER(S):
1638 * [I] infoPtr : valid pointer to the listview structure
1639 * [I] INT : item index
1641 * RETURN:
1642 * None
1644 static BOOL LISTVIEW_IsSelected(LISTVIEW_INFO *infoPtr, INT nItem)
1646 LISTVIEW_SELECTION selection;
1647 INT index;
1649 selection.upper = nItem;
1650 selection.lower = nItem;
1652 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1653 LISTVIEW_CompareSelectionRanges,
1654 0,DPAS_SORTED);
1655 if (index != -1)
1656 return TRUE;
1657 else
1658 return FALSE;
1661 /***
1662 * DESCRIPTION:
1663 * Removes all selection ranges
1665 * Parameters(s):
1666 * [I] infoPtr : valid pointer to the listview structure
1668 * RETURNS:
1669 * SUCCESS : TRUE
1670 * FAILURE : TRUE
1672 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
1674 LISTVIEW_SELECTION *selection;
1675 INT i;
1676 LVITEMW item;
1678 TRACE("()\n");
1680 ZeroMemory(&item,sizeof(item));
1681 item.stateMask = LVIS_SELECTED;
1685 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1686 if (selection)
1688 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1689 for (i = selection->lower; i<=selection->upper; i++)
1690 LISTVIEW_SetItemState(infoPtr,i,&item);
1691 LISTVIEW_RemoveSelectionRange(infoPtr,selection->lower,selection->upper);
1694 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1696 TRACE("done\n");
1697 return TRUE;
1701 * DESCRIPTION:
1702 * Removes a range selections.
1704 * PARAMETER(S):
1705 * [I] infoPtr : valid pointer to the listview structure
1706 * [I] INT : lower item index
1707 * [I] INT : upper item index
1709 * RETURN:
1710 * None
1712 static VOID LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lItem, INT uItem)
1714 LISTVIEW_SELECTION removeselection,*checkselection;
1715 INT index;
1717 removeselection.lower = lItem;
1718 removeselection.upper = uItem;
1720 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1721 LISTVIEW_PrintSelectionRanges(infoPtr);
1723 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1724 LISTVIEW_CompareSelectionRanges,
1725 0,0);
1727 if (index == -1)
1728 return;
1731 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1732 index);
1734 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1735 checkselection->upper);
1737 /* case 1: Same */
1738 if ((checkselection->upper == removeselection.upper) &&
1739 (checkselection->lower == removeselection.lower))
1741 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1742 TRACE("Case 1\n");
1744 /* case 2: engulf */
1745 else if (((checkselection->upper < removeselection.upper) &&
1746 (checkselection->lower > removeselection.lower))||
1747 ((checkselection->upper <= removeselection.upper) &&
1748 (checkselection->lower > removeselection.lower)) ||
1749 ((checkselection->upper < removeselection.upper) &&
1750 (checkselection->lower >= removeselection.lower)))
1753 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1754 /* do it again because others may also get caught */
1755 TRACE("Case 2\n");
1756 LISTVIEW_RemoveSelectionRange(infoPtr,lItem,uItem);
1758 /* case 3: overlap upper */
1759 else if ((checkselection->upper < removeselection.upper) &&
1760 (checkselection->lower < removeselection.lower))
1762 checkselection->upper = removeselection.lower - 1;
1763 TRACE("Case 3\n");
1764 LISTVIEW_RemoveSelectionRange(infoPtr,lItem,uItem);
1766 /* case 4: overlap lower */
1767 else if ((checkselection->upper > removeselection.upper) &&
1768 (checkselection->lower > removeselection.lower))
1770 checkselection->lower = removeselection.upper + 1;
1771 TRACE("Case 4\n");
1772 LISTVIEW_RemoveSelectionRange(infoPtr,lItem,uItem);
1774 /* case 5: fully internal */
1775 else if (checkselection->upper == removeselection.upper)
1776 checkselection->upper = removeselection.lower - 1;
1777 else if (checkselection->lower == removeselection.lower)
1778 checkselection->lower = removeselection.upper + 1;
1779 else
1781 /* bisect the range */
1782 LISTVIEW_SELECTION *newselection;
1784 newselection = (LISTVIEW_SELECTION *)
1785 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1786 newselection -> lower = checkselection->lower;
1787 newselection -> upper = removeselection.lower - 1;
1788 checkselection -> lower = removeselection.upper + 1;
1789 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1790 TRACE("Case 5\n");
1791 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1793 LISTVIEW_PrintSelectionRanges(infoPtr);
1797 * DESCRIPTION:
1798 * Updates the various indices after an item has been inserted or deleted.
1800 * PARAMETER(S):
1801 * [I] infoPtr : valid pointer to the listview structure
1802 * [I] INT : item index
1803 * [I] INT : Direction of shift, +1 or -1.
1805 * RETURN:
1806 * None
1808 static VOID LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
1810 LISTVIEW_SELECTION selection,*checkselection;
1811 INT index;
1813 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1815 selection.upper = nItem;
1816 selection.lower = nItem;
1818 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1819 LISTVIEW_CompareSelectionRanges,
1820 0,DPAS_SORTED|DPAS_INSERTAFTER);
1822 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1824 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1825 if ((checkselection->lower >= nItem)&&
1826 ((int)(checkselection->lower + direction) >= 0))
1827 checkselection->lower += direction;
1828 if ((checkselection->upper >= nItem)&&
1829 ((int)(checkselection->upper + direction) >= 0))
1830 checkselection->upper += direction;
1831 index ++;
1834 /* Note that the following will fail if direction != +1 and -1 */
1835 if (infoPtr->nSelectionMark > nItem)
1836 infoPtr->nSelectionMark += direction;
1837 else if (infoPtr->nSelectionMark == nItem)
1839 if (direction > 0)
1840 infoPtr->nSelectionMark += direction;
1841 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1842 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1845 if (infoPtr->nFocusedItem > nItem)
1846 infoPtr->nFocusedItem += direction;
1847 else if (infoPtr->nFocusedItem == nItem)
1849 if (direction > 0)
1850 infoPtr->nFocusedItem += direction;
1851 else
1853 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1854 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1855 if (infoPtr->nFocusedItem >= 0)
1856 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
1859 /* But we are not supposed to modify nHotItem! */
1864 * DESCRIPTION:
1865 * Adds a block of selections.
1867 * PARAMETER(S):
1868 * [I] infoPtr : valid pointer to the listview structure
1869 * [I] INT : item index
1871 * RETURN:
1872 * None
1874 static VOID LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
1876 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1877 INT nLast = max(infoPtr->nSelectionMark, nItem);
1878 INT i;
1879 LVITEMW item;
1881 if (nFirst == -1)
1882 nFirst = nItem;
1884 ZeroMemory(&item,sizeof(item));
1885 item.stateMask = LVIS_SELECTED;
1886 item.state = LVIS_SELECTED;
1888 for (i = nFirst; i <= nLast; i++)
1889 LISTVIEW_SetItemState(infoPtr,i,&item);
1891 LISTVIEW_SetItemFocus(infoPtr, nItem);
1892 infoPtr->nSelectionMark = nItem;
1896 /***
1897 * DESCRIPTION:
1898 * Adds a single selection.
1900 * PARAMETER(S):
1901 * [I] infoPtr : valid pointer to the listview structure
1902 * [I] INT : item index
1904 * RETURN:
1905 * None
1907 static VOID LISTVIEW_AddSelection(LISTVIEW_INFO *infoPtr, INT nItem)
1909 LVITEMW item;
1911 ZeroMemory(&item,sizeof(item));
1912 item.state = LVIS_SELECTED;
1913 item.stateMask = LVIS_SELECTED;
1915 LISTVIEW_SetItemState(infoPtr,nItem,&item);
1917 LISTVIEW_SetItemFocus(infoPtr, nItem);
1918 infoPtr->nSelectionMark = nItem;
1921 /***
1922 * DESCRIPTION:
1923 * Selects or unselects an item.
1925 * PARAMETER(S):
1926 * [I] infoPtr : valid pointer to the listview structure
1927 * [I] INT : item index
1929 * RETURN:
1930 * SELECT: TRUE
1931 * UNSELECT : FALSE
1933 static BOOL LISTVIEW_ToggleSelection(LISTVIEW_INFO *infoPtr, INT nItem)
1935 BOOL bResult;
1936 LVITEMW item;
1938 ZeroMemory(&item,sizeof(item));
1939 item.stateMask = LVIS_SELECTED;
1941 if (LISTVIEW_IsSelected(infoPtr,nItem))
1943 LISTVIEW_SetItemState(infoPtr,nItem,&item);
1944 bResult = FALSE;
1946 else
1948 item.state = LVIS_SELECTED;
1949 LISTVIEW_SetItemState(infoPtr,nItem,&item);
1950 bResult = TRUE;
1953 LISTVIEW_SetItemFocus(infoPtr, nItem);
1954 infoPtr->nSelectionMark = nItem;
1956 return bResult;
1959 /***
1960 * DESCRIPTION:
1961 * Selects items based on view coordinates.
1963 * PARAMETER(S):
1964 * [I] infoPtr : valid pointer to the listview structure
1965 * [I] RECT : selection rectangle
1967 * RETURN:
1968 * None
1970 static VOID LISTVIEW_SetSelectionRect(LISTVIEW_INFO *infoPtr, RECT rcSelRect)
1972 POINT ptItem;
1973 INT i;
1974 LVITEMW item;
1976 ZeroMemory(&item,sizeof(item));
1977 item.stateMask = LVIS_SELECTED;
1979 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1981 LISTVIEW_GetItemPosition(infoPtr, i, &ptItem);
1983 if (PtInRect(&rcSelRect, ptItem))
1984 item.state = LVIS_SELECTED;
1985 else
1986 item.state = 0;
1987 LISTVIEW_SetItemState(infoPtr,i,&item);
1991 /***
1992 * DESCRIPTION:
1993 * Sets a single group selection.
1995 * PARAMETER(S):
1996 * [I] infoPtr : valid pointer to the listview structure
1997 * [I] INT : item index
1999 * RETURN:
2000 * None
2002 static VOID LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2004 UINT uView = get_listview_type(infoPtr);
2005 LVITEMW item;
2007 ZeroMemory(&item,sizeof(item));
2008 item.stateMask = LVIS_SELECTED;
2010 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2012 INT i;
2013 INT nFirst, nLast;
2015 if (infoPtr->nSelectionMark == -1)
2017 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2019 else
2021 nFirst = min(infoPtr->nSelectionMark, nItem);
2022 nLast = max(infoPtr->nSelectionMark, nItem);
2025 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2027 if ((i < nFirst) || (i > nLast))
2028 item.state = 0;
2029 else
2030 item.state = LVIS_SELECTED;
2031 LISTVIEW_SetItemState(infoPtr,i,&item);
2034 else
2036 RECT rcItem;
2037 RECT rcSelMark;
2038 RECT rcSel;
2039 LISTVIEW_GetItemBoundBox(infoPtr, nItem, &rcItem);
2040 LISTVIEW_GetItemBoundBox(infoPtr, infoPtr->nSelectionMark, &rcSelMark);
2041 rcSel.left = min(rcSelMark.left, rcItem.left);
2042 rcSel.top = min(rcSelMark.top, rcItem.top);
2043 rcSel.right = max(rcSelMark.right, rcItem.right);
2044 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2045 LISTVIEW_SetSelectionRect(infoPtr, rcSel);
2046 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2047 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2048 infoPtr->nSelectionMark,
2049 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2050 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2054 LISTVIEW_SetItemFocus(infoPtr, nItem);
2057 /***
2058 * DESCRIPTION:
2059 * Manages the item focus.
2061 * PARAMETER(S):
2062 * [I] infoPtr : valid pointer to the listview structure
2063 * [I] INT : item index
2065 * RETURN:
2066 * TRUE : focused item changed
2067 * FALSE : focused item has NOT changed
2069 static BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2071 BOOL bResult = FALSE;
2072 LVITEMW lvItem;
2074 if (infoPtr->nFocusedItem != nItem)
2076 if (infoPtr->nFocusedItem >= 0)
2078 INT oldFocus = infoPtr->nFocusedItem;
2079 bResult = TRUE;
2080 infoPtr->nFocusedItem = -1;
2081 ZeroMemory(&lvItem, sizeof(lvItem));
2082 lvItem.stateMask = LVIS_FOCUSED;
2083 LISTVIEW_SetItemState(infoPtr, oldFocus, &lvItem);
2087 lvItem.state = LVIS_FOCUSED;
2088 lvItem.stateMask = LVIS_FOCUSED;
2089 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2091 infoPtr->nFocusedItem = nItem;
2092 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
2095 return bResult;
2098 /***
2099 * DESCRIPTION:
2100 * Sets a single selection.
2102 * PARAMETER(S):
2103 * [I] infoPtr : valid pointer to the listview structure
2104 * [I] INT : item index
2106 * RETURN:
2107 * None
2109 static VOID LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2111 LVITEMW lvItem;
2113 ZeroMemory(&lvItem, sizeof(lvItem));
2114 lvItem.stateMask = LVIS_FOCUSED;
2115 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &lvItem);
2117 LISTVIEW_RemoveAllSelections(infoPtr);
2119 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2120 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2121 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2123 infoPtr->nFocusedItem = nItem;
2124 infoPtr->nSelectionMark = nItem;
2127 /***
2128 * DESCRIPTION:
2129 * Set selection(s) with keyboard.
2131 * PARAMETER(S):
2132 * [I] infoPtr : valid pointer to the listview structure
2133 * [I] INT : item index
2135 * RETURN:
2136 * SUCCESS : TRUE (needs to be repainted)
2137 * FAILURE : FALSE (nothing has changed)
2139 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2141 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2142 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2143 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2144 BOOL bResult = FALSE;
2146 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2148 if (lStyle & LVS_SINGLESEL)
2150 bResult = TRUE;
2151 LISTVIEW_SetSelection(infoPtr, nItem);
2152 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2154 else
2156 if (wShift)
2158 bResult = TRUE;
2159 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2161 else if (wCtrl)
2163 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2165 else
2167 bResult = TRUE;
2168 LISTVIEW_SetSelection(infoPtr, nItem);
2169 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2174 return bResult;
2177 /***
2178 * DESCRIPTION:
2179 * Called when the mouse is being actively tracked and has hovered for a specified
2180 * amount of time
2182 * PARAMETER(S):
2183 * [I] infoPtr : valid pointer to the listview structure
2184 * [I] fwKeys : key indicator
2185 * [I] pts : mouse position
2187 * RETURN:
2188 * 0 if the message was processed, non-zero if there was an error
2190 * INFO:
2191 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2192 * over the item for a certain period of time.
2195 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2197 POINT pt = { pts.x, pts.y };
2199 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2200 /* FIXME: select the item under the cursor */
2201 LISTVIEW_MouseSelection(infoPtr, pt);
2204 return 0;
2207 /***
2208 * DESCRIPTION:
2209 * Called whenever WM_MOUSEMOVE is received.
2211 * PARAMETER(S):
2212 * [I] infoPtr : valid pointer to the listview structure
2213 * [I] fwKeys : key indicator
2214 * [I] pts : mouse position
2216 * RETURN:
2217 * 0 if the message is processed, non-zero if there was an error
2219 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2221 TRACKMOUSEEVENT trackinfo;
2223 /* see if we are supposed to be tracking mouse hovering */
2224 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2225 /* fill in the trackinfo struct */
2226 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2227 trackinfo.dwFlags = TME_QUERY;
2228 trackinfo.hwndTrack = infoPtr->hwndSelf;
2229 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2231 /* see if we are already tracking this hwnd */
2232 _TrackMouseEvent(&trackinfo);
2234 if(!(trackinfo.dwFlags & TME_HOVER)) {
2235 trackinfo.dwFlags = TME_HOVER;
2237 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2238 _TrackMouseEvent(&trackinfo);
2242 return 0;
2245 /***
2246 * DESCRIPTION:
2247 * Selects an item based on coordinates.
2249 * PARAMETER(S):
2250 * [I] infoPtr : valid pointer to the listview structure
2251 * [I] pt : mouse click ccordinates
2253 * RETURN:
2254 * SUCCESS : item index
2255 * FAILURE : -1
2257 static INT LISTVIEW_MouseSelection(LISTVIEW_INFO *infoPtr, POINT pt)
2259 LONG uView = get_listview_type(infoPtr);
2260 INT i, topindex, bottomindex;
2261 RECT rcItem;
2263 topindex = LISTVIEW_GetTopIndex(infoPtr);
2264 if (uView == LVS_REPORT) {
2265 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
2266 bottomindex = min(bottomindex, GETITEMCOUNT(infoPtr));
2267 } else {
2268 bottomindex = GETITEMCOUNT(infoPtr);
2271 for (i = topindex; i < bottomindex; i++)
2273 rcItem.left = LVIR_SELECTBOUNDS;
2274 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
2276 if (PtInRect(&rcItem, pt)) return i;
2280 return -1;
2283 /***
2284 * DESCRIPTION:
2285 * Removes a column.
2287 * PARAMETER(S):
2288 * [IO] HDPA : dynamic pointer array handle
2289 * [I] INT : column index (subitem index)
2291 * RETURN:
2292 * SUCCCESS : TRUE
2293 * FAILURE : FALSE
2295 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2297 BOOL bResult = TRUE;
2298 HDPA hdpaSubItems;
2299 INT i;
2301 for (i = 0; i < hdpaItems->nItemCount; i++)
2303 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2304 if (hdpaSubItems != NULL)
2306 if (!LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem))
2308 bResult = FALSE;
2313 return bResult;
2316 /***
2317 * DESCRIPTION:
2318 * Removes a subitem at a given position.
2320 * PARAMETER(S):
2321 * [IO] HDPA : dynamic pointer array handle
2322 * [I] INT : subitem index
2324 * RETURN:
2325 * SUCCCESS : TRUE
2326 * FAILURE : FALSE
2328 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2330 LISTVIEW_SUBITEM *lpSubItem;
2331 INT i;
2333 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2335 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2336 if (lpSubItem != NULL)
2338 if (lpSubItem->iSubItem == nSubItem)
2340 /* free string */
2341 if (is_textW(lpSubItem->pszText))
2342 COMCTL32_Free(lpSubItem->pszText);
2344 /* free item */
2345 COMCTL32_Free(lpSubItem);
2347 /* free dpa memory */
2348 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2349 return FALSE;
2351 else if (lpSubItem->iSubItem > nSubItem)
2352 return TRUE;
2356 return TRUE;
2359 /***
2360 * DESCRIPTION:
2361 * Compares the item information.
2363 * PARAMETER(S):
2364 * [I] LISTVIEW_ITEM *: destination item
2365 * [I] LPLVITEM : source item
2366 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2368 * RETURN:
2369 * SUCCCESS : TRUE (EQUAL)
2370 * FAILURE : FALSE (NOT EQUAL)
2372 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2374 UINT uChanged = 0;
2376 if ((lpItem != NULL) && (lpLVItem != NULL))
2378 if (lpLVItem->mask & LVIF_STATE)
2380 if ((lpItem->state & lpLVItem->stateMask) !=
2381 (lpLVItem->state & lpLVItem->stateMask))
2382 uChanged |= LVIF_STATE;
2385 if (lpLVItem->mask & LVIF_IMAGE)
2387 if (lpItem->iImage != lpLVItem->iImage)
2388 uChanged |= LVIF_IMAGE;
2391 if (lpLVItem->mask & LVIF_PARAM)
2393 if (lpItem->lParam != lpLVItem->lParam)
2394 uChanged |= LVIF_PARAM;
2397 if (lpLVItem->mask & LVIF_INDENT)
2399 if (lpItem->iIndent != lpLVItem->iIndent)
2400 uChanged |= LVIF_INDENT;
2403 if (lpLVItem->mask & LVIF_TEXT)
2405 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2407 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2408 uChanged |= LVIF_TEXT;
2410 else
2412 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2414 uChanged |= LVIF_TEXT;
2416 else
2418 if (lpLVItem->pszText)
2420 if (lpItem->pszText)
2422 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2423 if (pszText && strcmpW(pszText, lpItem->pszText))
2424 uChanged |= LVIF_TEXT;
2425 textfreeT(pszText, isW);
2427 else
2429 uChanged |= LVIF_TEXT;
2432 else
2434 if (lpItem->pszText)
2435 uChanged |= LVIF_TEXT;
2441 return uChanged;
2444 /***
2445 * DESCRIPTION:
2446 * Initializes item attributes.
2448 * PARAMETER(S):
2449 * [I] infoPtr : valid pointer to the listview structure
2450 * [O] LISTVIEW_ITEM *: destination item
2451 * [I] LPLVITEM : source item
2452 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2454 * RETURN:
2455 * SUCCCESS : TRUE
2456 * FAILURE : FALSE
2458 static BOOL LISTVIEW_InitItemT(LISTVIEW_INFO *infoPtr, LISTVIEW_ITEM *lpItem,
2459 LPLVITEMW lpLVItem, BOOL isW)
2461 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2462 BOOL bResult = FALSE;
2464 if ((lpItem != NULL) && (lpLVItem != NULL))
2466 bResult = TRUE;
2468 if (lpLVItem->mask & LVIF_STATE)
2470 lpItem->state &= ~lpLVItem->stateMask;
2471 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2474 if (lpLVItem->mask & LVIF_IMAGE)
2475 lpItem->iImage = lpLVItem->iImage;
2477 if (lpLVItem->mask & LVIF_PARAM)
2478 lpItem->lParam = lpLVItem->lParam;
2480 if (lpLVItem->mask & LVIF_INDENT)
2481 lpItem->iIndent = lpLVItem->iIndent;
2483 if (lpLVItem->mask & LVIF_TEXT)
2485 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2487 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2488 return FALSE;
2490 if (is_textW(lpItem->pszText))
2491 COMCTL32_Free(lpItem->pszText);
2493 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2495 else
2496 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2500 return bResult;
2503 /***
2504 * DESCRIPTION:
2505 * Initializes subitem attributes.
2507 * NOTE: The documentation specifies that the operation fails if the user
2508 * tries to set the indent of a subitem.
2510 * PARAMETER(S):
2511 * [I] infoPtr : valid pointer to the listview structure
2512 * [O] LISTVIEW_SUBITEM *: destination subitem
2513 * [I] LPLVITEM : source subitem
2514 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2516 * RETURN:
2517 * SUCCCESS : TRUE
2518 * FAILURE : FALSE
2520 static BOOL LISTVIEW_InitSubItemT(LISTVIEW_INFO *infoPtr, LISTVIEW_SUBITEM *lpSubItem,
2521 LPLVITEMW lpLVItem, BOOL isW)
2523 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2524 BOOL bResult = FALSE;
2526 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2528 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2530 if (!(lpLVItem->mask & LVIF_INDENT))
2532 bResult = TRUE;
2534 lpSubItem->iSubItem = lpLVItem->iSubItem;
2536 if (lpLVItem->mask & LVIF_IMAGE)
2537 lpSubItem->iImage = lpLVItem->iImage;
2539 if (lpLVItem->mask & LVIF_TEXT)
2541 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2543 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2544 return FALSE;
2546 if (is_textW(lpSubItem->pszText))
2547 COMCTL32_Free(lpSubItem->pszText);
2549 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2551 else
2552 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2557 return bResult;
2560 /***
2561 * DESCRIPTION:
2562 * Adds a subitem at a given position (column index).
2564 * PARAMETER(S):
2565 * [I] infoPtr : valid pointer to the listview structure
2566 * [I] LPLVITEM : new subitem atttributes
2567 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2569 * RETURN:
2570 * SUCCESS : TRUE
2571 * FAILURE : FALSE
2573 static BOOL LISTVIEW_AddSubItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2575 LISTVIEW_SUBITEM *lpSubItem = NULL;
2576 BOOL bResult = FALSE;
2577 HDPA hdpaSubItems;
2578 INT nPosition, nItem;
2579 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2581 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2583 if (lStyle & LVS_OWNERDATA)
2584 return FALSE;
2586 if (lpLVItem != NULL)
2588 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2589 if (hdpaSubItems != NULL)
2591 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2592 if (lpSubItem != NULL)
2594 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2595 if (LISTVIEW_InitSubItemT(infoPtr, lpSubItem, lpLVItem, isW))
2597 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2598 lpSubItem->iSubItem);
2599 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2600 if (nItem != -1) bResult = TRUE;
2606 /* cleanup if unsuccessful */
2607 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2609 return bResult;
2612 /***
2613 * DESCRIPTION:
2614 * Finds the dpa insert position (array index).
2616 * PARAMETER(S):
2617 * [I] infoPtr : valid pointer to the listview structure
2618 * [I] INT : subitem index
2620 * RETURN:
2621 * SUCCESS : TRUE
2622 * FAILURE : FALSE
2624 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2626 LISTVIEW_SUBITEM *lpSubItem;
2627 INT i;
2629 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2631 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2632 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2633 return i;
2636 return hdpaSubItems->nItemCount;
2639 /***
2640 * DESCRIPTION:
2641 * Retrieves a listview subitem at a given position (column index).
2643 * PARAMETER(S):
2644 * [I] infoPtr : valid pointer to the listview structure
2645 * [I] INT : subitem index
2647 * RETURN:
2648 * SUCCESS : TRUE
2649 * FAILURE : FALSE
2651 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2653 LISTVIEW_SUBITEM *lpSubItem;
2654 INT i;
2656 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2658 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2659 if (lpSubItem != NULL)
2661 if (lpSubItem->iSubItem == nSubItem)
2662 return lpSubItem;
2663 else if (lpSubItem->iSubItem > nSubItem)
2664 return NULL;
2668 return NULL;
2671 /***
2672 * DESCRIPTION:
2673 * Sets item attributes.
2675 * PARAMETER(S):
2676 * [I] infoPtr : valid pointer to the listview structure
2677 * [I] LPLVITEM : new item atttributes
2678 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2680 * RETURN:
2681 * SUCCESS : TRUE
2682 * FAILURE : FALSE
2684 static BOOL LISTVIEW_SetMainItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2686 BOOL bResult = FALSE;
2687 HDPA hdpaSubItems;
2688 LISTVIEW_ITEM *lpItem;
2689 NMLISTVIEW nmlv;
2690 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2691 UINT uChanged;
2692 UINT uView = lStyle & LVS_TYPEMASK;
2693 INT item_width;
2694 RECT rcItem;
2696 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2698 if (lStyle & LVS_OWNERDATA)
2700 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2702 LVITEMW itm;
2704 ZeroMemory(&itm, sizeof(itm));
2705 itm.mask = LVIF_STATE | LVIF_PARAM;
2706 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2707 itm.iItem = lpLVItem->iItem;
2708 itm.iSubItem = 0;
2709 ListView_GetItemW(infoPtr->hwndSelf, &itm);
2712 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2713 nmlv.uNewState = lpLVItem->state;
2714 nmlv.uOldState = itm.state;
2715 nmlv.uChanged = LVIF_STATE;
2716 nmlv.lParam = itm.lParam;
2717 nmlv.iItem = lpLVItem->iItem;
2719 if ((itm.state & lpLVItem->stateMask) !=
2720 (lpLVItem->state & lpLVItem->stateMask))
2723 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent
2724 * by LVS_OWERNDATA list controls
2726 if (lpLVItem->stateMask & LVIS_FOCUSED)
2728 if (lpLVItem->state & LVIS_FOCUSED)
2729 infoPtr->nFocusedItem = lpLVItem->iItem;
2730 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2731 infoPtr->nFocusedItem = -1;
2733 if (lpLVItem->stateMask & LVIS_SELECTED)
2735 if (lpLVItem->state & LVIS_SELECTED)
2737 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2738 LISTVIEW_AddSelectionRange(infoPtr,lpLVItem->iItem,lpLVItem->iItem);
2740 else
2741 LISTVIEW_RemoveSelectionRange(infoPtr,lpLVItem->iItem,
2742 lpLVItem->iItem);
2745 listview_notify(infoPtr, LVN_ITEMCHANGED, &nmlv);
2747 rcItem.left = LVIR_BOUNDS;
2748 LISTVIEW_GetItemRect(infoPtr, lpLVItem->iItem, &rcItem);
2749 if (!infoPtr->bIsDrawing)
2750 InvalidateRect(infoPtr->hwndSelf, &rcItem, TRUE);
2752 return TRUE;
2754 return FALSE;
2757 if (lpLVItem != NULL)
2759 if (lpLVItem->iSubItem == 0)
2761 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2762 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2764 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2765 if (lpItem != NULL)
2767 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2768 nmlv.lParam = lpItem->lParam;
2769 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2770 if (uChanged != 0)
2772 if (uChanged & LVIF_STATE)
2774 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2775 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2777 if (nmlv.uNewState & LVIS_SELECTED)
2780 * This is redundant if called through SetSelection
2782 * however is required if the used directly calls SetItem
2783 * to set the selection.
2785 if (lStyle & LVS_SINGLESEL)
2786 LISTVIEW_RemoveAllSelections(infoPtr);
2788 LISTVIEW_AddSelectionRange(infoPtr,lpLVItem->iItem,
2789 lpLVItem->iItem);
2791 else if (lpLVItem->stateMask & LVIS_SELECTED)
2793 LISTVIEW_RemoveSelectionRange(infoPtr,lpLVItem->iItem,
2794 lpLVItem->iItem);
2796 if (nmlv.uNewState & LVIS_FOCUSED)
2799 * This is a fun hoop to jump to try to catch if
2800 * the user is calling us directly to call focus or if
2801 * this function is being called as a result of a
2802 * SetItemFocus call.
2804 if (infoPtr->nFocusedItem >= 0)
2805 LISTVIEW_SetItemFocus(infoPtr, lpLVItem->iItem);
2809 nmlv.uChanged = uChanged;
2810 nmlv.iItem = lpLVItem->iItem;
2811 nmlv.lParam = lpItem->lParam;
2812 /* send LVN_ITEMCHANGING notification */
2813 listview_notify(infoPtr, LVN_ITEMCHANGING, &nmlv);
2815 /* copy information */
2816 bResult = LISTVIEW_InitItemT(infoPtr, lpItem, lpLVItem, isW);
2818 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2819 based on the width of the items text */
2820 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2822 item_width = LISTVIEW_GetStringWidthT(infoPtr, lpItem->pszText, TRUE);
2824 if(item_width > infoPtr->nItemWidth)
2825 infoPtr->nItemWidth = item_width;
2828 /* send LVN_ITEMCHANGED notification */
2829 listview_notify(infoPtr, LVN_ITEMCHANGED, &nmlv);
2831 else
2833 bResult = TRUE;
2836 if (uChanged)
2838 rcItem.left = LVIR_BOUNDS;
2839 LISTVIEW_GetItemRect(infoPtr, lpLVItem->iItem, &rcItem);
2840 if (!infoPtr->bIsDrawing)
2841 InvalidateRect(infoPtr->hwndSelf, &rcItem, TRUE);
2848 return bResult;
2851 /***
2852 * DESCRIPTION:
2853 * Sets subitem attributes.
2855 * PARAMETER(S):
2856 * [I] infoPtr : valid pointer to the listview structure
2857 * [I] LPLVITEM : new subitem atttributes
2858 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2860 * RETURN:
2861 * SUCCESS : TRUE
2862 * FAILURE : FALSE
2864 static BOOL LISTVIEW_SetSubItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2866 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2867 BOOL bResult = FALSE;
2868 HDPA hdpaSubItems;
2869 LISTVIEW_SUBITEM *lpSubItem;
2870 RECT rcItem;
2872 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2874 if (lStyle & LVS_OWNERDATA)
2875 return FALSE;
2877 if (lpLVItem != NULL)
2879 if (lpLVItem->iSubItem > 0)
2881 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2882 if (hdpaSubItems != NULL)
2884 /* set subitem only if column is present */
2885 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2887 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2888 if (lpSubItem != NULL)
2889 bResult = LISTVIEW_InitSubItemT(infoPtr, lpSubItem, lpLVItem, isW);
2890 else
2891 bResult = LISTVIEW_AddSubItemT(infoPtr, lpLVItem, isW);
2893 rcItem.left = LVIR_BOUNDS;
2894 LISTVIEW_GetItemRect(infoPtr, lpLVItem->iItem, &rcItem);
2895 InvalidateRect(infoPtr->hwndSelf, &rcItem, FALSE);
2901 return bResult;
2904 /***
2905 * DESCRIPTION:
2906 * Sets item attributes.
2908 * PARAMETER(S):
2909 * [I] infoPtr : valid pointer to the listview structure
2910 * [I] LPLVITEM : new item atttributes
2911 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2913 * RETURN:
2914 * SUCCESS : TRUE
2915 * FAILURE : FALSE
2917 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2920 if (!lpLVItem || lpLVItem->iItem < 0 ||
2921 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2922 return FALSE;
2923 if (lpLVItem->iSubItem == 0)
2924 return LISTVIEW_SetMainItemT(infoPtr, lpLVItem, isW);
2925 else
2926 return LISTVIEW_SetSubItemT(infoPtr, lpLVItem, isW);
2929 /***
2930 * DESCRIPTION:
2931 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2933 * PARAMETER(S):
2934 * [I] infoPtr : valid pointer to the listview structure
2936 * RETURN:
2937 * item index
2939 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2941 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2942 UINT uView = lStyle & LVS_TYPEMASK;
2943 INT nItem = 0;
2944 SCROLLINFO scrollInfo;
2946 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2947 scrollInfo.cbSize = sizeof(SCROLLINFO);
2948 scrollInfo.fMask = SIF_POS;
2950 if (uView == LVS_LIST)
2952 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2953 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2955 else if (uView == LVS_REPORT)
2957 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2958 nItem = scrollInfo.nPos;
2961 return nItem;
2964 /***
2965 * DESCRIPTION:
2966 * Draws a subitem.
2968 * PARAMETER(S):
2969 * [I] infoPtr : valid pointer to the listview structure
2970 * [I] HDC : device context handle
2971 * [I] INT : item index
2972 * [I] INT : subitem index
2973 * [I] RECT * : clipping rectangle
2975 * RETURN:
2976 * None
2978 static VOID LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem,
2979 RECT rcItem, BOOL Selected)
2981 WCHAR szDispText[DISP_TEXT_SIZE];
2982 LVITEMW lvItem;
2983 LVCOLUMNW lvColumn;
2984 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2985 RECT rcTemp;
2986 INT textLeft;
2987 INT nLabelWidth = 0;
2989 TRACE("(hdc=%x, nItem=%d, nSubItem=%d)\n", hdc,
2990 nItem, nSubItem);
2992 /* get information needed for drawing the item */
2993 ZeroMemory(&lvItem, sizeof(lvItem));
2994 lvItem.mask = LVIF_TEXT;
2995 lvItem.iItem = nItem;
2996 lvItem.iSubItem = nSubItem;
2997 lvItem.cchTextMax = DISP_TEXT_SIZE;
2998 lvItem.pszText = szDispText;
2999 *lvItem.pszText = '\0';
3000 LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE);
3001 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3003 ZeroMemory(&lvColumn, sizeof(lvColumn));
3004 lvColumn.mask = LVCF_FMT;
3005 LISTVIEW_GetColumnT(infoPtr, nSubItem, &lvColumn, TRUE);
3006 textLeft = rcItem.left;
3007 TRACE("lvColumn.fmt=%d\n", lvColumn.fmt);
3008 if (lvColumn.fmt != LVCFMT_LEFT)
3010 if ((nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE)))
3012 if (lvColumn.fmt == LVCFMT_RIGHT)
3013 textLeft = rcItem.right - nLabelWidth;
3014 else
3015 textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
3020 /* redraw the background of the item */
3021 rcTemp = rcItem;
3022 if(infoPtr->nColumnCount == (nSubItem + 1))
3023 rcTemp.right = infoPtr->rcList.right;
3024 else
3025 rcTemp.right += WIDTH_PADDING;
3027 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3029 /* set item colors */
3030 if (ListView_GetItemState(infoPtr->hwndSelf,nItem,LVIS_SELECTED) && Selected)
3032 if (infoPtr->bFocus)
3034 SetBkColor(hdc, comctl32_color.clrHighlight);
3035 SetTextColor(hdc, comctl32_color.clrHighlightText);
3037 else
3039 SetBkColor(hdc, comctl32_color.clr3dFace);
3040 SetTextColor(hdc, comctl32_color.clrBtnText);
3043 else
3045 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3047 SetBkMode(hdc, TRANSPARENT);
3048 textoutOptions &= ~ETO_OPAQUE;
3050 else
3052 SetBkMode(hdc, OPAQUE);
3053 SetBkColor(hdc, infoPtr->clrTextBk);
3056 SetTextColor(hdc, infoPtr->clrText);
3059 TRACE("drawing text %s, l=%d, t=%d, rect=(%d,%d)-(%d,%d)\n",
3060 debugstr_w(lvItem.pszText), textLeft, rcItem.top,
3061 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3062 ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
3063 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3065 if (Selected)
3067 /* fill in the gap */
3068 RECT rec;
3069 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3071 CopyRect(&rec,&rcItem);
3072 rec.left = rec.right;
3073 rec.right = rec.left+REPORT_MARGINX;
3074 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3075 &rec, NULL, 0, NULL);
3077 CopyRect(&rec,&rcItem);
3078 rec.right = rec.left;
3079 rec.left = rec.left - REPORT_MARGINX;
3080 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3081 &rec, NULL, 0, NULL);
3086 /***
3087 * DESCRIPTION:
3088 * Draws an item.
3090 * PARAMETER(S):
3091 * [I] infoPtr : valid pointer to the listview structure
3092 * [I] HDC : device context handle
3093 * [I] INT : item index
3094 * [I] RECT * : clipping rectangle
3096 * RETURN:
3097 * None
3099 static VOID LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3101 WCHAR szDispText[DISP_TEXT_SIZE];
3102 INT nLabelWidth;
3103 LVITEMW lvItem;
3104 INT nMixMode;
3105 DWORD dwBkColor;
3106 DWORD dwTextColor,dwTextX;
3107 BOOL bImage = FALSE;
3108 INT iBkMode = -1;
3109 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3110 RECT rcTemp;
3112 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
3115 /* get information needed for drawing the item */
3116 ZeroMemory(&lvItem, sizeof(lvItem));
3117 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3118 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3119 lvItem.iItem = nItem;
3120 lvItem.iSubItem = 0;
3121 lvItem.cchTextMax = DISP_TEXT_SIZE;
3122 lvItem.pszText = szDispText;
3123 *lvItem.pszText = '\0';
3124 LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE);
3125 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3127 /* redraw the background of the item */
3128 rcTemp = rcItem;
3129 if(infoPtr->nColumnCount == (nItem + 1))
3130 rcTemp.right = infoPtr->rcList.right;
3131 else
3132 rcTemp.right+=WIDTH_PADDING;
3134 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3136 /* do indent */
3137 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3139 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3141 if (SuggestedFocus)
3142 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3145 /* state icons */
3146 if (infoPtr->himlState != NULL)
3148 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3149 if (uStateImage > 0)
3151 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3152 rcItem.top, ILD_NORMAL);
3155 rcItem.left += infoPtr->iconSize.cx;
3156 if (SuggestedFocus)
3157 SuggestedFocus->left += infoPtr->iconSize.cx;
3158 bImage = TRUE;
3161 /* small icons */
3162 if (infoPtr->himlSmall != NULL)
3164 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) &&
3165 (lvItem.iImage>=0))
3167 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3168 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3169 rcItem.top, ILD_SELECTED);
3171 else if (lvItem.iImage>=0)
3173 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3174 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3175 rcItem.top, ILD_NORMAL);
3178 rcItem.left += infoPtr->iconSize.cx;
3180 if (SuggestedFocus)
3181 SuggestedFocus->left += infoPtr->iconSize.cx;
3182 bImage = TRUE;
3185 /* Don't bother painting item being edited */
3186 if (infoPtr->bEditing && lvItem.state & LVIS_FOCUSED && !FullSelect)
3187 return;
3189 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus))
3191 /* set item colors */
3192 dwBkColor = SetBkColor(hdc, comctl32_color.clrHighlight);
3193 dwTextColor = SetTextColor(hdc, comctl32_color.clrHighlightText);
3194 /* set raster mode */
3195 nMixMode = SetROP2(hdc, R2_XORPEN);
3197 else if ((GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3198 (lvItem.state & LVIS_SELECTED) && (!infoPtr->bFocus))
3200 dwBkColor = SetBkColor(hdc, comctl32_color.clr3dFace);
3201 dwTextColor = SetTextColor(hdc, comctl32_color.clrBtnText);
3202 /* set raster mode */
3203 nMixMode = SetROP2(hdc, R2_COPYPEN);
3205 else
3207 /* set item colors */
3208 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3210 dwBkColor = GetBkColor(hdc);
3211 iBkMode = SetBkMode(hdc, TRANSPARENT);
3212 textoutOptions &= ~ETO_OPAQUE;
3214 else
3216 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3217 iBkMode = SetBkMode(hdc, OPAQUE);
3220 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3221 /* set raster mode */
3222 nMixMode = SetROP2(hdc, R2_COPYPEN);
3225 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
3226 if (rcItem.left + nLabelWidth < rcItem.right)
3228 if (!FullSelect)
3229 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3230 if (bImage)
3231 rcItem.right += IMAGE_PADDING;
3234 /* draw label */
3235 dwTextX = rcItem.left + 1;
3236 if (bImage)
3237 dwTextX += IMAGE_PADDING;
3239 if (lvItem.pszText) {
3240 TRACE("drawing text rect=(%d,%d)-(%d,%d)\n",
3241 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3242 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3243 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3246 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3248 /* fill in the gap */
3249 RECT rec;
3250 CopyRect(&rec,&rcItem);
3251 rec.left = rec.right;
3252 rec.right = rec.left+REPORT_MARGINX;
3253 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3254 &rec, NULL, 0, NULL);
3257 if (!FullSelect)
3258 CopyRect(SuggestedFocus,&rcItem);
3260 if (nMixMode != 0)
3262 SetROP2(hdc, R2_COPYPEN);
3263 SetBkColor(hdc, dwBkColor);
3264 SetTextColor(hdc, dwTextColor);
3265 if (iBkMode != -1)
3266 SetBkMode(hdc, iBkMode);
3270 /***
3271 * DESCRIPTION:
3272 * Draws an item when in large icon display mode.
3274 * PARAMETER(S):
3275 * [I] infoPtr : valid pointer to the listview structure
3276 * [I] HDC : device context handle
3277 * [I] INT : item index
3278 * [I] RECT : clipping rectangle
3279 * [O] RECT * : The text rectangle about which to draw the focus
3281 * RETURN:
3282 * None
3284 static VOID LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem,
3285 RECT *SuggestedFocus)
3287 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3288 LVITEMW lvItem;
3289 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3290 DT_EDITCONTROL ;
3291 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3292 RECT rcTemp;
3294 TRACE("(hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3295 hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3297 /* get information needed for drawing the item */
3298 ZeroMemory(&lvItem, sizeof(lvItem));
3299 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3300 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3301 lvItem.iItem = nItem;
3302 lvItem.iSubItem = 0;
3303 lvItem.cchTextMax = DISP_TEXT_SIZE;
3304 lvItem.pszText = szDispText;
3305 *lvItem.pszText = '\0';
3306 LISTVIEW_GetItemW(infoPtr, &lvItem, FALSE);
3307 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3309 /* redraw the background of the item */
3310 rcTemp = rcItem;
3311 if(infoPtr->nColumnCount == (nItem + 1))
3312 rcTemp.right = infoPtr->rcList.right;
3313 else
3314 rcTemp.right+=WIDTH_PADDING;
3315 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3317 TRACE("background rect (%d,%d)-(%d,%d)\n",
3318 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3320 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3323 /* Figure out text colours etc. depending on state
3324 * At least the following states exist; there may be more.
3325 * Many items may be selected
3326 * At most one item may have the focus
3327 * The application may not actually be active currently
3328 * 1. The item is not selected in any way
3329 * 2. The cursor is flying over the icon or text and the text is being
3330 * expanded because it is not fully displayed currently.
3331 * 3. The item is selected and is focussed, i.e. the user has not clicked
3332 * in the blank area of the window, and the window (or application?)
3333 * still has the focus.
3334 * 4. As 3 except that a different window has the focus
3335 * 5. The item is the selected item of all the items, but the user has
3336 * clicked somewhere else on the window.
3337 * Only a few of these are handled currently. In particular 2 is not yet
3338 * handled since we do not support the functionality currently (or at least
3339 * we didn't when I wrote this)
3342 if (lvItem.state & LVIS_SELECTED)
3344 /* set item colors */
3345 SetBkColor(hdc, comctl32_color.clrHighlight);
3346 SetTextColor(hdc, comctl32_color.clrHighlightText);
3347 SetBkMode (hdc, OPAQUE);
3348 /* set raster mode */
3349 SetROP2(hdc, R2_XORPEN);
3350 /* When exactly is it in XOR? while being dragged? */
3352 else
3354 /* set item colors */
3355 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3357 SetBkMode(hdc, TRANSPARENT);
3359 else
3361 SetBkMode(hdc, OPAQUE);
3362 SetBkColor(hdc, infoPtr->clrTextBk);
3365 SetTextColor(hdc, infoPtr->clrText);
3366 /* set raster mode */
3367 SetROP2(hdc, R2_COPYPEN);
3370 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3371 * wrapping and long words split.
3372 * In cases 1 and 4 only a portion of the text is displayed with word
3373 * wrapping and both word and end ellipsis. (I don't yet know about path
3374 * ellipsis)
3376 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3377 DT_NOCLIP
3379 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3381 /* draw the icon */
3382 if (infoPtr->himlNormal != NULL)
3384 if (lvItem.iImage >= 0)
3386 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3387 rcItem.top,
3388 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3392 /* Draw the text below the icon */
3394 /* Don't bother painting item being edited */
3395 if ((infoPtr->bEditing && (lvItem.state & LVIS_FOCUSED)) ||
3396 !lstrlenW(lvItem.pszText))
3398 SetRectEmpty(SuggestedFocus);
3399 return;
3402 /* Since rcItem.left is left point of icon, compute left point of item box */
3403 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3404 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3405 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3406 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3407 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3408 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3409 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3410 infoPtr->rcList.left, infoPtr->rcList.top,
3411 infoPtr->rcList.right, infoPtr->rcList.bottom,
3412 infoPtr->rcView.left, infoPtr->rcView.top,
3413 infoPtr->rcView.right, infoPtr->rcView.bottom);
3415 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3416 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3419 /* draw label */
3421 /* I am sure of most of the uFormat values. However I am not sure about
3422 * whether we need or do not need the following:
3423 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3424 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3425 * We certainly do not need
3426 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3427 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3430 /* If the text is being drawn without clipping (i.e. the full text) then we
3431 * need to jump through a few hoops to ensure that it all gets displayed and
3432 * that the background is complete
3434 if (uFormat & DT_NOCLIP)
3436 RECT rcBack=rcItem;
3437 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3438 int dx, dy, old_wid, new_wid;
3439 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3440 /* Microsoft, in their great wisdom, have decided that the rectangle
3441 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3442 * not the location. So we have to do the centring ourselves (and take
3443 * responsibility for agreeing off-by-one consistency with them).
3445 old_wid = rcItem.right-rcItem.left;
3446 new_wid = rcBack.right - rcBack.left;
3447 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3448 dy = rcBack.top - rcItem.top;
3449 OffsetRect (&rcItem, dx, dy);
3450 FillRect(hdc, &rcItem, hBrush);
3451 DeleteObject(hBrush);
3453 /* else ? What if we are losing the focus? will we not get a complete
3454 * background?
3456 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3458 CopyRect(SuggestedFocus, &rcItem);
3461 /***
3462 * DESCRIPTION:
3463 * Draws listview items when in report display mode.
3465 * PARAMETER(S):
3466 * [I] infoPtr : valid pointer to the listview structure
3467 * [I] HDC : device context handle
3469 * RETURN:
3470 * None
3472 static VOID LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3474 SCROLLINFO scrollInfo;
3475 INT nDrawPosY = infoPtr->rcList.top;
3476 INT nColumnCount;
3477 RECT rcItem, rcTemp;
3478 INT j;
3479 INT nItem;
3480 INT nLast;
3481 BOOL FullSelected;
3482 DWORD cditemmode = CDRF_DODEFAULT;
3483 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
3484 INT scrollOffset;
3486 TRACE("\n");
3488 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3490 /* add 1 for displaying a partial item at the bottom */
3491 nLast = min(nItem + LISTVIEW_GetCountPerColumn(infoPtr) + 1, GETITEMCOUNT(infoPtr));
3493 /* send cache hint notification */
3494 if (lStyle & LVS_OWNERDATA)
3496 NMLVCACHEHINT nmlv;
3498 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
3499 nmlv.hdr.idFrom = GetWindowLongW(infoPtr->hwndSelf,GWL_ID);
3500 nmlv.hdr.code = LVN_ODCACHEHINT;
3501 nmlv.iFrom = nItem;
3502 nmlv.iTo = nLast;
3504 SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3505 (LPARAM)&nmlv);
3508 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3509 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3510 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3512 /* clear the background of any part of the control that doesn't contain items */
3513 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3514 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3516 /* nothing to draw */
3517 if(GETITEMCOUNT(infoPtr) == 0)
3518 return;
3520 /* Get scroll bar info once before loop */
3521 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3522 scrollInfo.cbSize = sizeof(SCROLLINFO);
3523 scrollInfo.fMask = SIF_POS;
3524 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
3525 scrollOffset = scrollInfo.nPos;
3527 for (; nItem < nLast; nItem++)
3529 RECT SuggestedFocusRect;
3531 /* Do Owner Draw */
3532 if (lStyle & LVS_OWNERDRAWFIXED)
3534 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3535 DRAWITEMSTRUCT dis;
3536 LVITEMW item;
3537 RECT br;
3539 TRACE("Owner Drawn\n");
3540 dis.CtlType = ODT_LISTVIEW;
3541 dis.CtlID = uID;
3542 dis.itemID = nItem;
3543 dis.itemAction = ODA_DRAWENTIRE;
3544 dis.itemState = 0;
3546 if (LISTVIEW_IsSelected(infoPtr,nItem)) dis.itemState|=ODS_SELECTED;
3547 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3549 dis.hwndItem = infoPtr->hwndSelf;
3550 dis.hDC = hdc;
3552 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3554 dis.rcItem.left = -scrollOffset;
3555 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3556 dis.rcItem.top = nDrawPosY;
3557 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3559 ZeroMemory(&item,sizeof(item));
3560 item.iItem = nItem;
3561 item.mask = LVIF_PARAM;
3562 ListView_GetItemW(infoPtr->hwndSelf, &item);
3564 dis.itemData = item.lParam;
3566 if (SendMessageW(GetParent(infoPtr->hwndSelf),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3568 nDrawPosY += infoPtr->nItemHeight;
3569 continue;
3573 if (FullSelected)
3575 RECT ir,br;
3577 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3578 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3580 ir.left += REPORT_MARGINX;
3581 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3582 ir.top = nDrawPosY;
3583 ir.bottom = ir.top + infoPtr->nItemHeight;
3585 CopyRect(&SuggestedFocusRect,&ir);
3588 for (j = 0; j < nColumnCount; j++)
3590 if (cdmode & CDRF_NOTIFYITEMDRAW)
3591 cditemmode = LISTVIEW_SendCustomDrawItemNotify (infoPtr, hdc, nItem, j,
3592 CDDS_ITEMPREPAINT);
3593 if (cditemmode & CDRF_SKIPDEFAULT)
3594 continue;
3596 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3598 rcItem.left += REPORT_MARGINX;
3599 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3600 rcItem.top = nDrawPosY;
3601 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3603 /* Offset the Scroll Bar Pos */
3604 rcItem.left -= scrollOffset;
3605 rcItem.right -= scrollOffset;
3607 if (j == 0)
3609 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem, FullSelected,
3610 &SuggestedFocusRect);
3612 else
3614 LISTVIEW_DrawSubItem(infoPtr, hdc, nItem, j, rcItem, FullSelected);
3617 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3618 LISTVIEW_SendCustomDrawItemNotify(infoPtr, hdc, nItem, 0,
3619 CDDS_ITEMPOSTPAINT);
3622 * Draw Focus Rect
3624 if (LISTVIEW_GetItemState(infoPtr,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3626 BOOL rop=FALSE;
3627 if (FullSelected && LISTVIEW_GetItemState(infoPtr,nItem,LVIS_SELECTED))
3628 rop = SetROP2(hdc, R2_XORPEN);
3630 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3631 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3633 if (rop)
3634 SetROP2(hdc, R2_COPYPEN);
3636 nDrawPosY += infoPtr->nItemHeight;
3640 /***
3641 * DESCRIPTION:
3642 * Retrieves the number of items that can fit vertically in the client area.
3644 * PARAMETER(S):
3645 * [I] infoPtr : valid pointer to the listview structure
3647 * RETURN:
3648 * Number of items per row.
3650 static INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
3652 UINT uView = get_listview_type(infoPtr);
3653 INT nCountPerRow = 0;
3655 if (uView != LVS_REPORT)
3656 nCountPerRow = (infoPtr->rcList.right - infoPtr->rcList.left) / infoPtr->nItemWidth;
3658 return max(nCountPerRow, 1);
3661 /***
3662 * DESCRIPTION:
3663 * Retrieves the number of items that can fit horizontally in the client
3664 * area.
3666 * PARAMETER(S):
3667 * [I] infoPtr : valid pointer to the listview structure
3669 * RETURN:
3670 * Number of items per column.
3672 static INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
3674 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3675 INT nCountPerColumn = 1;
3677 if (nListHeight > 0)
3679 TRACE("rcList=(%d,%d)-(%d,%d), nItemHeight=%d, CYHSCROLL=%d\n",
3680 infoPtr->rcList.left,infoPtr->rcList.top,
3681 infoPtr->rcList.right,infoPtr->rcList.bottom,
3682 infoPtr->nItemHeight,
3683 GetSystemMetrics(SM_CYHSCROLL));
3684 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3685 if (nCountPerColumn == 0) nCountPerColumn = 1;
3688 return nCountPerColumn;
3691 /***
3692 * DESCRIPTION:
3693 * Retrieves the number of columns needed to display all the items when in
3694 * list display mode.
3696 * PARAMETER(S):
3697 * [I] infoPtr : valid pointer to the listview structure
3699 * RETURN:
3700 * Number of columns.
3702 static INT LISTVIEW_GetColumnCount(LISTVIEW_INFO *infoPtr)
3704 INT nColumnCount = 0;
3706 if (get_listview_type(infoPtr) == LVS_LIST) {
3707 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3708 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3711 return nColumnCount;
3715 /***
3716 * DESCRIPTION:
3717 * Draws listview items when in list display mode.
3719 * PARAMETER(S):
3720 * [I] infoPtr : valid pointer to the listview structure
3721 * [I] HDC : device context handle
3723 * RETURN:
3724 * None
3726 static VOID LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3728 RECT rcItem, FocusRect, rcTemp;
3729 INT i, j;
3730 INT nItem;
3731 INT nColumnCount;
3732 INT nCountPerColumn;
3733 INT nItemWidth = infoPtr->nItemWidth;
3734 INT nItemHeight = infoPtr->nItemHeight;
3735 DWORD cditemmode = CDRF_DODEFAULT;
3737 /* get number of fully visible columns */
3738 nColumnCount = LISTVIEW_GetColumnCount(infoPtr);
3739 infoPtr->nColumnCount = nColumnCount;
3740 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
3741 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3742 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3743 nColumnCount, nCountPerColumn, nItem);
3745 /* paint the background of the control that doesn't contain any items */
3746 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3747 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3749 /* nothing to draw, return here */
3750 if(GETITEMCOUNT(infoPtr) == 0)
3751 return;
3753 for (i = 0; i < nColumnCount; i++)
3755 for (j = 0; j < nCountPerColumn; j++, nItem++)
3757 if (nItem >= GETITEMCOUNT(infoPtr))
3758 return;
3760 if (cdmode & CDRF_NOTIFYITEMDRAW)
3761 cditemmode = LISTVIEW_SendCustomDrawItemNotify (infoPtr, hdc, nItem, 0,
3762 CDDS_ITEMPREPAINT);
3763 if (cditemmode & CDRF_SKIPDEFAULT)
3764 continue;
3766 rcItem.top = j * nItemHeight;
3767 rcItem.left = i * nItemWidth;
3768 rcItem.bottom = rcItem.top + nItemHeight;
3769 rcItem.right = rcItem.left + nItemWidth;
3770 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem, FALSE, &FocusRect);
3772 * Draw Focus Rect
3774 if (LISTVIEW_GetItemState(infoPtr,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3775 Rectangle(hdc, FocusRect.left, FocusRect.top,
3776 FocusRect.right,FocusRect.bottom);
3778 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3779 LISTVIEW_SendCustomDrawItemNotify(infoPtr, hdc, nItem, 0,
3780 CDDS_ITEMPOSTPAINT);
3786 /***
3787 * DESCRIPTION:
3788 * Draws listview items when in icon or small icon display mode.
3790 * PARAMETER(S):
3791 * [I] infoPtr : valid pointer to the listview structure
3792 * [I] HDC : device context handle
3794 * RETURN:
3795 * None
3797 static VOID LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL bSmall, DWORD cdmode)
3799 POINT ptPosition;
3800 POINT ptOrigin;
3801 RECT rcItem, SuggestedFocus, rcTemp;
3802 INT i;
3803 DWORD cditemmode = CDRF_DODEFAULT;
3805 TRACE("\n");
3806 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3807 /* DrawItem from erasing the incorrect background area */
3809 /* paint the background of the control that doesn't contain any items */
3810 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3811 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3813 /* nothing to draw, return here */
3814 if(GETITEMCOUNT(infoPtr) == 0)
3815 return;
3817 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
3818 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3820 if (cdmode & CDRF_NOTIFYITEMDRAW)
3821 cditemmode = LISTVIEW_SendCustomDrawItemNotify (infoPtr, hdc, i, 0,
3822 CDDS_ITEMPREPAINT);
3823 if (cditemmode & CDRF_SKIPDEFAULT)
3824 continue;
3826 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3827 ptPosition.x += ptOrigin.x;
3828 ptPosition.y += ptOrigin.y;
3830 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3832 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3834 if (ptPosition.y < infoPtr->rcList.bottom)
3836 if (ptPosition.x < infoPtr->rcList.right)
3838 rcItem.top = ptPosition.y;
3839 rcItem.left = ptPosition.x;
3840 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3841 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3842 if (bSmall)
3843 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem, FALSE, &SuggestedFocus);
3844 else
3845 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem, &SuggestedFocus);
3847 * Draw Focus Rect
3849 if (LISTVIEW_GetItemState(infoPtr,i,LVIS_FOCUSED) &&
3850 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3851 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3852 SuggestedFocus.right,SuggestedFocus.bottom);
3857 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3858 LISTVIEW_SendCustomDrawItemNotify(infoPtr, hdc, i, 0,
3859 CDDS_ITEMPOSTPAINT);
3863 /***
3864 * DESCRIPTION:
3865 * Draws listview items.
3867 * PARAMETER(S):
3868 * [I] infoPtr : valid pointer to the listview structure
3869 * [I] HDC : device context handle
3871 * RETURN:
3872 * NoneX
3874 static VOID LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3876 UINT uView = get_listview_type(infoPtr);
3877 HFONT hOldFont;
3878 HPEN hPen, hOldPen;
3879 DWORD cdmode;
3880 RECT rect;
3882 LISTVIEW_DUMP(infoPtr);
3884 infoPtr->bIsDrawing = TRUE;
3886 GetClientRect(infoPtr->hwndSelf, &rect);
3887 cdmode = LISTVIEW_SendCustomDrawNotify(infoPtr,CDDS_PREPAINT,hdc,rect);
3889 if (cdmode == CDRF_SKIPDEFAULT) return;
3891 /* select font */
3892 hOldFont = SelectObject(hdc, infoPtr->hFont);
3894 /* select the dotted pen (for drawing the focus box) */
3895 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3896 hOldPen = SelectObject(hdc, hPen);
3898 /* select transparent brush (for drawing the focus box) */
3899 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3901 TRACE("\n");
3902 if (uView == LVS_LIST)
3903 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3904 else if (uView == LVS_REPORT)
3905 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3906 else if (uView == LVS_SMALLICON)
3907 LISTVIEW_RefreshIcon(infoPtr, hdc, TRUE, cdmode);
3908 else if (uView == LVS_ICON)
3909 LISTVIEW_RefreshIcon(infoPtr, hdc, FALSE, cdmode);
3911 /* unselect objects */
3912 SelectObject(hdc, hOldFont);
3913 SelectObject(hdc, hOldPen);
3915 /* delete pen */
3916 DeleteObject(hPen);
3918 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3919 LISTVIEW_SendCustomDrawNotify(infoPtr, CDDS_POSTPAINT, hdc, rect);
3921 infoPtr->bIsDrawing = FALSE;
3925 /***
3926 * DESCRIPTION:
3927 * Calculates the approximate width and height of a given number of items.
3929 * PARAMETER(S):
3930 * [I] infoPtr : valid pointer to the listview structure
3931 * [I] INT : number of items
3932 * [I] INT : width
3933 * [I] INT : height
3935 * RETURN:
3936 * Returns a DWORD. The width in the low word and the height in high word.
3938 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3939 WORD wWidth, WORD wHeight)
3941 UINT uView = get_listview_type(infoPtr);
3942 INT nItemCountPerColumn = 1;
3943 INT nColumnCount = 0;
3944 DWORD dwViewRect = 0;
3946 if (nItemCount == -1)
3947 nItemCount = GETITEMCOUNT(infoPtr);
3949 if (uView == LVS_LIST)
3951 if (wHeight == 0xFFFF)
3953 /* use current height */
3954 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3957 if (wHeight < infoPtr->nItemHeight)
3958 wHeight = infoPtr->nItemHeight;
3960 if (nItemCount > 0)
3962 if (infoPtr->nItemHeight > 0)
3964 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3965 if (nItemCountPerColumn == 0)
3966 nItemCountPerColumn = 1;
3968 if (nItemCount % nItemCountPerColumn != 0)
3969 nColumnCount = nItemCount / nItemCountPerColumn;
3970 else
3971 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3975 /* Microsoft padding magic */
3976 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3977 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3979 dwViewRect = MAKELONG(wWidth, wHeight);
3981 else if (uView == LVS_REPORT)
3982 FIXME("uView == LVS_REPORT: not implemented\n");
3983 else if (uView == LVS_SMALLICON)
3984 FIXME("uView == LVS_SMALLICON: not implemented\n");
3985 else if (uView == LVS_ICON)
3986 FIXME("uView == LVS_ICON: not implemented\n");
3988 return dwViewRect;
3991 /***
3992 * DESCRIPTION:
3993 * Arranges listview items in icon display mode.
3995 * PARAMETER(S):
3996 * [I] infoPtr : valid pointer to the listview structure
3997 * [I] INT : alignment code
3999 * RETURN:
4000 * SUCCESS : TRUE
4001 * FAILURE : FALSE
4003 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
4005 UINT uView = get_listview_type(infoPtr);
4006 BOOL bResult = FALSE;
4008 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4010 switch (nAlignCode)
4012 case LVA_ALIGNLEFT:
4013 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4014 break;
4015 case LVA_ALIGNTOP:
4016 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4017 break;
4018 case LVA_DEFAULT:
4019 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4020 break;
4021 case LVA_SNAPTOGRID:
4022 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4023 break;
4027 return bResult;
4030 /* << LISTVIEW_CreateDragImage >> */
4033 /***
4034 * DESCRIPTION:
4035 * Removes all listview items and subitems.
4037 * PARAMETER(S):
4038 * [I] infoPtr : valid pointer to the listview structure
4040 * RETURN:
4041 * SUCCESS : TRUE
4042 * FAILURE : FALSE
4044 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4046 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
4047 UINT uView = lStyle & LVS_TYPEMASK;
4048 LISTVIEW_ITEM *lpItem;
4049 LISTVIEW_SUBITEM *lpSubItem;
4050 NMLISTVIEW nmlv;
4051 BOOL bSuppress;
4052 BOOL bResult = FALSE;
4053 HDPA hdpaSubItems;
4055 TRACE("()\n");
4057 LISTVIEW_RemoveAllSelections(infoPtr);
4058 infoPtr->nSelectionMark=-1;
4059 infoPtr->nFocusedItem=-1;
4060 /* But we are supposed to leave nHotItem as is! */
4062 if (lStyle & LVS_OWNERDATA)
4064 infoPtr->hdpaItems->nItemCount = 0;
4065 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
4066 return TRUE;
4069 if (GETITEMCOUNT(infoPtr) > 0)
4071 INT i, j;
4073 /* send LVN_DELETEALLITEMS notification */
4074 /* verify if subsequent LVN_DELETEITEM notifications should be
4075 suppressed */
4076 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4077 nmlv.iItem = -1;
4078 bSuppress = listview_notify(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4080 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4082 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4083 if (hdpaSubItems != NULL)
4085 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4087 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4088 if (lpSubItem != NULL)
4090 /* free subitem string */
4091 if (is_textW(lpSubItem->pszText))
4092 COMCTL32_Free(lpSubItem->pszText);
4094 /* free subitem */
4095 COMCTL32_Free(lpSubItem);
4099 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4100 if (lpItem != NULL)
4102 if (!bSuppress)
4104 /* send LVN_DELETEITEM notification */
4105 nmlv.iItem = i;
4106 nmlv.lParam = lpItem->lParam;
4107 listview_notify(infoPtr, LVN_DELETEITEM, &nmlv);
4110 /* free item string */
4111 if (is_textW(lpItem->pszText))
4112 COMCTL32_Free(lpItem->pszText);
4114 /* free item */
4115 COMCTL32_Free(lpItem);
4118 DPA_Destroy(hdpaSubItems);
4122 /* reinitialize listview memory */
4123 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4125 /* align items (set position of each item) */
4126 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4128 if (lStyle & LVS_ALIGNLEFT)
4130 LISTVIEW_AlignLeft(infoPtr);
4132 else
4134 LISTVIEW_AlignTop(infoPtr);
4138 LISTVIEW_UpdateScroll(infoPtr);
4140 /* invalidate client area (optimization needed) */
4141 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
4144 return bResult;
4147 /***
4148 * DESCRIPTION:
4149 * Removes a column from the listview control.
4151 * PARAMETER(S):
4152 * [I] infoPtr : valid pointer to the listview structure
4153 * [I] INT : column index
4155 * RETURN:
4156 * SUCCESS : TRUE
4157 * FAILURE : FALSE
4159 static LRESULT LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4161 UINT uView = get_listview_type(infoPtr);
4162 UINT uOwnerData = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_OWNERDATA;
4163 BOOL bResult = FALSE;
4165 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4167 bResult = uOwnerData ? TRUE : LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4169 /* Need to reset the item width when deleting a column */
4170 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
4172 /* reset scroll parameters */
4173 if (uView == LVS_REPORT)
4175 /* update scrollbar(s) */
4176 LISTVIEW_UpdateScroll(infoPtr);
4178 /* refresh client area */
4179 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
4183 return bResult;
4186 /***
4187 * DESCRIPTION:
4188 * Removes an item from the listview control.
4190 * PARAMETER(S):
4191 * [I] infoPtr : valid pointer to the listview structure
4192 * [I] INT : item index
4194 * RETURN:
4195 * SUCCESS : TRUE
4196 * FAILURE : FALSE
4198 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4200 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
4201 UINT uView = lStyle & LVS_TYPEMASK;
4202 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
4203 NMLISTVIEW nmlv;
4204 BOOL bResult = FALSE;
4205 HDPA hdpaSubItems;
4206 LISTVIEW_ITEM *lpItem;
4207 LISTVIEW_SUBITEM *lpSubItem;
4208 INT i;
4209 LVITEMW item;
4211 TRACE("(nItem=%d)\n", nItem);
4214 /* First, send LVN_DELETEITEM notification. */
4215 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4216 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
4217 nmlv.hdr.idFrom = lCtrlId;
4218 nmlv.hdr.code = LVN_DELETEITEM;
4219 nmlv.iItem = nItem;
4220 SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
4221 (LPARAM)&nmlv);
4224 /* remove it from the selection range */
4225 ZeroMemory(&item,sizeof(item));
4226 item.stateMask = LVIS_SELECTED;
4227 LISTVIEW_SetItemState(infoPtr,nItem,&item);
4229 if (lStyle & LVS_OWNERDATA)
4231 infoPtr->hdpaItems->nItemCount --;
4232 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
4233 return TRUE;
4236 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4238 /* initialize memory */
4239 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4241 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4242 if (hdpaSubItems != NULL)
4244 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4246 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4247 if (lpSubItem != NULL)
4249 /* free item string */
4250 if (is_textW(lpSubItem->pszText))
4251 COMCTL32_Free(lpSubItem->pszText);
4253 /* free item */
4254 COMCTL32_Free(lpSubItem);
4258 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4259 if (lpItem != NULL)
4261 /* free item string */
4262 if (is_textW(lpItem->pszText))
4263 COMCTL32_Free(lpItem->pszText);
4265 /* free item */
4266 COMCTL32_Free(lpItem);
4269 bResult = DPA_Destroy(hdpaSubItems);
4272 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
4274 /* align items (set position of each item) */
4275 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4277 if (lStyle & LVS_ALIGNLEFT)
4278 LISTVIEW_AlignLeft(infoPtr);
4279 else
4280 LISTVIEW_AlignTop(infoPtr);
4283 LISTVIEW_UpdateScroll(infoPtr);
4285 /* refresh client area */
4286 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
4289 return bResult;
4293 /***
4294 * DESCRIPTION:
4295 * Callback implementation for editlabel control
4297 * PARAMETER(S):
4298 * [I] infoPtr : valid pointer to the listview structure
4299 * [I] pszText : modified text
4300 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4302 * RETURN:
4303 * SUCCESS : TRUE
4304 * FAILURE : FALSE
4306 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4308 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
4309 NMLVDISPINFOW dispInfo;
4310 LISTVIEW_ITEM *lpItem;
4311 HDPA hdpaSubItems;
4312 LISTVIEW_ITEM lvItemRef;
4313 LVITEMW item;
4314 BOOL bResult = TRUE;
4315 INT nItem = infoPtr->nEditLabelItem;
4317 TRACE("(pszText=%s, nItem=%d, isW=%d)\n", debugtext_t(pszText, isW), nItem, isW);
4319 infoPtr->bEditing = FALSE;
4320 if (!(lStyle & LVS_OWNERDATA))
4322 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4323 return FALSE;
4325 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4326 return FALSE;
4328 else
4330 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4331 ZeroMemory(&item,sizeof(item));
4332 item.iItem = nItem;
4333 item.iSubItem = 0;
4334 item.mask = LVIF_PARAM | LVIF_STATE;
4335 ListView_GetItemW(infoPtr->hwndSelf, &item);
4336 lvItemRef.state = item.state;
4337 lvItemRef.iImage = item.iImage;
4338 lvItemRef.lParam = item.lParam;
4339 lpItem = &lvItemRef;
4342 ZeroMemory(&dispInfo, sizeof(dispInfo));
4343 dispInfo.item.mask = 0;
4344 dispInfo.item.iItem = nItem;
4345 dispInfo.item.state = lpItem->state;
4346 dispInfo.item.stateMask = 0;
4347 dispInfo.item.pszText = pszText;
4348 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4349 dispInfo.item.iImage = lpItem->iImage;
4350 dispInfo.item.lParam = lpItem->lParam;
4352 /* Do we need to update the Item Text */
4353 if(dispinfo_notifyT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW))
4354 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4355 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4357 return bResult;
4360 /***
4361 * DESCRIPTION:
4362 * Begin in place editing of specified list view item
4364 * PARAMETER(S):
4365 * [I] infoPtr : valid pointer to the listview structure
4366 * [I] INT : item index
4367 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4369 * RETURN:
4370 * SUCCESS : TRUE
4371 * FAILURE : FALSE
4373 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4375 NMLVDISPINFOW dispInfo;
4376 RECT rect;
4377 LISTVIEW_ITEM *lpItem;
4378 HWND hedit;
4379 HDPA hdpaSubItems;
4380 WCHAR szDispText[DISP_TEXT_SIZE];
4381 LVITEMW lvItem;
4382 LISTVIEW_ITEM lvItemRef;
4383 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
4385 if (~GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_EDITLABELS)
4386 return FALSE;
4388 infoPtr->nEditLabelItem = nItem;
4390 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4392 /* Is the EditBox still there, if so remove it */
4393 if(infoPtr->hwndEdit != 0)
4395 SetFocus(infoPtr->hwndSelf);
4396 infoPtr->hwndEdit = 0;
4399 LISTVIEW_SetSelection(infoPtr, nItem);
4400 LISTVIEW_SetItemFocus(infoPtr, nItem);
4402 if (!(lStyle & LVS_OWNERDATA))
4404 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4405 return 0;
4407 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4408 return 0;
4410 else
4412 LVITEMW item;
4413 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4414 ZeroMemory(&item, sizeof(item));
4415 item.iItem = nItem;
4416 item.iSubItem = 0;
4417 item.mask = LVIF_PARAM | LVIF_STATE;
4418 ListView_GetItemW(infoPtr->hwndSelf, &item);
4419 lvItemRef.iImage = item.iImage;
4420 lvItemRef.state = item.state;
4421 lvItemRef.lParam = item.lParam;
4422 lpItem = &lvItemRef;
4425 /* get information needed for drawing the item */
4426 ZeroMemory(&lvItem, sizeof(lvItem));
4427 lvItem.mask = LVIF_TEXT;
4428 lvItem.iItem = nItem;
4429 lvItem.iSubItem = 0;
4430 lvItem.cchTextMax = DISP_TEXT_SIZE;
4431 lvItem.pszText = szDispText;
4432 *lvItem.pszText = '\0';
4433 LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, isW);
4435 ZeroMemory(&dispInfo, sizeof(dispInfo));
4436 dispInfo.item.mask = 0;
4437 dispInfo.item.iItem = nItem;
4438 dispInfo.item.state = lpItem->state;
4439 dispInfo.item.stateMask = 0;
4440 dispInfo.item.pszText = lvItem.pszText;
4441 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4442 dispInfo.item.iImage = lpItem->iImage;
4443 dispInfo.item.lParam = lpItem->lParam;
4445 if (dispinfo_notifyT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4446 return 0;
4448 rect.left = LVIR_LABEL;
4449 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect))
4450 return 0;
4452 if (!(hedit = CreateEditLabelT(infoPtr, szDispText, WS_VISIBLE,
4453 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW)))
4454 return 0;
4456 infoPtr->hwndEdit = hedit;
4458 ShowWindow(infoPtr->hwndEdit,SW_NORMAL);
4459 infoPtr->bEditing = TRUE;
4460 SetFocus(infoPtr->hwndEdit);
4461 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4462 return infoPtr->hwndEdit;
4466 /***
4467 * DESCRIPTION:
4468 * Ensures the specified item is visible, scrolling into view if necessary.
4470 * PARAMETER(S):
4471 * [I] infoPtr : valid pointer to the listview structure
4472 * [I] INT : item index
4473 * [I] BOOL : partially or entirely visible
4475 * RETURN:
4476 * SUCCESS : TRUE
4477 * FAILURE : FALSE
4479 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4481 UINT uView = get_listview_type(infoPtr);
4482 INT nScrollPosHeight = 0;
4483 INT nScrollPosWidth = 0;
4484 SCROLLINFO scrollInfo;
4485 RECT rcItem;
4486 BOOL bRedraw = FALSE;
4488 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4489 scrollInfo.cbSize = sizeof(SCROLLINFO);
4490 scrollInfo.fMask = SIF_POS;
4492 /* ALWAYS bPartial == FALSE, FOR NOW! */
4494 rcItem.left = LVIR_BOUNDS;
4495 if (LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem))
4497 if (rcItem.left < infoPtr->rcList.left)
4499 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4501 /* scroll left */
4502 bRedraw = TRUE;
4503 if (uView == LVS_LIST)
4505 nScrollPosWidth = infoPtr->nItemWidth;
4506 rcItem.left += infoPtr->rcList.left;
4508 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4510 nScrollPosWidth = 1;
4511 rcItem.left += infoPtr->rcList.left;
4514 /* When in LVS_REPORT view, the scroll position should
4515 not be updated. */
4516 if (nScrollPosWidth != 0)
4518 if (rcItem.left % nScrollPosWidth == 0)
4519 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4520 else
4521 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4523 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
4527 else if (rcItem.right > infoPtr->rcList.right)
4529 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4531 /* scroll right */
4532 bRedraw = TRUE;
4533 if (uView == LVS_LIST)
4535 rcItem.right -= infoPtr->rcList.right;
4536 nScrollPosWidth = infoPtr->nItemWidth;
4538 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4540 rcItem.right -= infoPtr->rcList.right;
4541 nScrollPosWidth = 1;
4544 /* When in LVS_REPORT view, the scroll position should
4545 not be updated. */
4546 if (nScrollPosWidth != 0)
4548 if (rcItem.right % nScrollPosWidth == 0)
4549 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4550 else
4551 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4553 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
4558 if (rcItem.top < infoPtr->rcList.top)
4560 /* scroll up */
4561 bRedraw = TRUE;
4562 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4564 if (uView == LVS_REPORT)
4566 rcItem.top -= infoPtr->rcList.top;
4567 nScrollPosHeight = infoPtr->nItemHeight;
4569 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4571 nScrollPosHeight = 1;
4572 rcItem.top += infoPtr->rcList.top;
4575 if (rcItem.top % nScrollPosHeight == 0)
4576 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4577 else
4578 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4580 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
4583 else if (rcItem.bottom > infoPtr->rcList.bottom)
4585 /* scroll down */
4586 bRedraw = TRUE;
4587 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4589 if (uView == LVS_REPORT)
4591 rcItem.bottom -= infoPtr->rcList.bottom;
4592 nScrollPosHeight = infoPtr->nItemHeight;
4594 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4596 nScrollPosHeight = 1;
4597 rcItem.bottom -= infoPtr->rcList.bottom;
4600 if (rcItem.bottom % nScrollPosHeight == 0)
4601 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4602 else
4603 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4605 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
4610 if(bRedraw)
4611 InvalidateRect(infoPtr->hwndSelf,NULL,TRUE);
4612 return TRUE;
4615 /***
4616 * DESCRIPTION:
4617 * Retrieves the nearest item, given a position and a direction.
4619 * PARAMETER(S):
4620 * [I] infoPtr : valid pointer to the listview structure
4621 * [I] POINT : start position
4622 * [I] UINT : direction
4624 * RETURN:
4625 * Item index if successdful, -1 otherwise.
4627 static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDirection)
4629 LV_INTHIT lvIntHit;
4630 INT nItem = -1;
4631 RECT rcView;
4633 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4634 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4635 ((vkDirection == VK_UP) ? "VK_UP" :
4636 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4638 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
4640 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4641 LISTVIEW_GetOrigin(infoPtr, &lvIntHit.ht.pt);
4642 lvIntHit.ht.pt.x += pt.x;
4643 lvIntHit.ht.pt.y += pt.y;
4645 if (vkDirection == VK_DOWN)
4646 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4647 else if (vkDirection == VK_UP)
4648 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4649 else if (vkDirection == VK_LEFT)
4650 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4651 else if (vkDirection == VK_RIGHT)
4652 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4654 if (!PtInRect(&rcView, lvIntHit.ht.pt))
4655 return -1;
4656 else
4658 nItem = LISTVIEW_SuperHitTestItem(infoPtr, &lvIntHit, TRUE);
4659 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4663 return nItem;
4666 /***
4667 * DESCRIPTION:
4668 * Searches for an item with specific characteristics.
4670 * PARAMETER(S):
4671 * [I] hwnd : window handle
4672 * [I] nStart : base item index
4673 * [I] lpFindInfo : item information to look for
4675 * RETURN:
4676 * SUCCESS : index of item
4677 * FAILURE : -1
4679 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4680 LPLVFINDINFOW lpFindInfo)
4682 POINT ptItem;
4683 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4684 LVITEMW lvItem;
4685 BOOL bWrap = FALSE;
4686 INT nItem = nStart;
4687 INT nLast = GETITEMCOUNT(infoPtr);
4689 if ((nItem >= -1) && (lpFindInfo != NULL))
4691 ZeroMemory(&lvItem, sizeof(lvItem));
4693 if (lpFindInfo->flags & LVFI_PARAM)
4695 lvItem.mask |= LVIF_PARAM;
4698 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4700 lvItem.mask |= LVIF_TEXT;
4701 lvItem.pszText = szDispText;
4702 lvItem.cchTextMax = DISP_TEXT_SIZE;
4705 if (lpFindInfo->flags & LVFI_WRAP)
4706 bWrap = TRUE;
4708 if (lpFindInfo->flags & LVFI_NEARESTXY)
4710 ptItem.x = lpFindInfo->pt.x;
4711 ptItem.y = lpFindInfo->pt.y;
4714 while (1)
4716 while (nItem < nLast)
4718 if (lpFindInfo->flags & LVFI_NEARESTXY)
4720 nItem = LISTVIEW_GetNearestItem(infoPtr, ptItem,
4721 lpFindInfo->vkDirection);
4722 if (nItem != -1)
4724 /* get position of the new item index */
4725 if (!ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &ptItem))
4726 return -1;
4728 else
4729 return -1;
4731 else
4733 nItem++;
4736 lvItem.iItem = nItem;
4737 lvItem.iSubItem = 0;
4738 if (LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE))
4740 if (lvItem.mask & LVIF_TEXT)
4742 if (lpFindInfo->flags & LVFI_PARTIAL)
4744 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4745 continue;
4747 else
4749 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4750 continue;
4754 if (lvItem.mask & LVIF_PARAM)
4756 if (lpFindInfo->lParam != lvItem.lParam)
4757 continue;
4760 return nItem;
4764 if (bWrap)
4766 nItem = -1;
4767 nLast = nStart + 1;
4768 bWrap = FALSE;
4770 else
4772 return -1;
4777 return -1;
4780 /***
4781 * DESCRIPTION:
4782 * Searches for an item with specific characteristics.
4784 * PARAMETER(S):
4785 * [I] hwnd : window handle
4786 * [I] nStart : base item index
4787 * [I] lpFindInfo : item information to look for
4789 * RETURN:
4790 * SUCCESS : index of item
4791 * FAILURE : -1
4793 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4794 LPLVFINDINFOA lpFindInfo)
4796 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4797 LVFINDINFOW fiw;
4798 LRESULT res;
4800 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4801 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4802 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4803 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4804 return res;
4807 /***
4808 * DESCRIPTION:
4809 * Retrieves the background image of the listview control.
4811 * PARAMETER(S):
4812 * [I] infoPtr : valid pointer to the listview structure
4813 * [O] LPLVMKBIMAGE : background image attributes
4815 * RETURN:
4816 * SUCCESS : TRUE
4817 * FAILURE : FALSE
4819 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4820 /* { */
4821 /* FIXME (listview, "empty stub!\n"); */
4822 /* return FALSE; */
4823 /* } */
4825 /***
4826 * DESCRIPTION:
4827 * Retrieves column attributes.
4829 * PARAMETER(S):
4830 * [I] infoPtr : valid pointer to the listview structure
4831 * [I] INT : column index
4832 * [IO] LPLVCOLUMNW : column information
4833 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4834 * otherwise it is in fact a LPLVCOLUMNA
4836 * RETURN:
4837 * SUCCESS : TRUE
4838 * FAILURE : FALSE
4840 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4842 HDITEMW hdi;
4843 BOOL bResult = FALSE;
4845 if (lpColumn != NULL)
4848 /* initialize memory */
4849 ZeroMemory(&hdi, sizeof(hdi));
4851 if (lpColumn->mask & LVCF_FMT)
4852 hdi.mask |= HDI_FORMAT;
4854 if (lpColumn->mask & LVCF_WIDTH)
4855 hdi.mask |= HDI_WIDTH;
4857 if (lpColumn->mask & LVCF_TEXT)
4859 hdi.mask |= HDI_TEXT;
4860 hdi.cchTextMax = lpColumn->cchTextMax;
4861 hdi.pszText = lpColumn->pszText;
4864 if (lpColumn->mask & LVCF_IMAGE)
4865 hdi.mask |= HDI_IMAGE;
4867 if (lpColumn->mask & LVCF_ORDER)
4868 hdi.mask |= HDI_ORDER;
4870 if (isW)
4871 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4872 else
4873 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4875 if (bResult)
4877 if (lpColumn->mask & LVCF_FMT)
4879 lpColumn->fmt = 0;
4881 if (hdi.fmt & HDF_LEFT)
4882 lpColumn->fmt |= LVCFMT_LEFT;
4883 else if (hdi.fmt & HDF_RIGHT)
4884 lpColumn->fmt |= LVCFMT_RIGHT;
4885 else if (hdi.fmt & HDF_CENTER)
4886 lpColumn->fmt |= LVCFMT_CENTER;
4888 if (hdi.fmt & HDF_IMAGE)
4889 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4891 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4892 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4895 if (lpColumn->mask & LVCF_WIDTH)
4896 lpColumn->cx = hdi.cxy;
4898 if (lpColumn->mask & LVCF_IMAGE)
4899 lpColumn->iImage = hdi.iImage;
4901 if (lpColumn->mask & LVCF_ORDER)
4902 lpColumn->iOrder = hdi.iOrder;
4904 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4905 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4910 return bResult;
4914 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4916 INT i;
4918 if (!lpiArray)
4919 return FALSE;
4921 /* FIXME: little hack */
4922 for (i = 0; i < iCount; i++)
4923 lpiArray[i] = i;
4925 return TRUE;
4928 /***
4929 * DESCRIPTION:
4930 * Retrieves the column width.
4932 * PARAMETER(S):
4933 * [I] infoPtr : valid pointer to the listview structure
4934 * [I] int : column index
4936 * RETURN:
4937 * SUCCESS : column width
4938 * FAILURE : zero
4940 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4942 UINT uView = get_listview_type(infoPtr);
4943 INT nColumnWidth = 0;
4944 HDITEMW hdi;
4946 if (uView == LVS_LIST)
4948 nColumnWidth = infoPtr->nItemWidth;
4950 else if (uView == LVS_REPORT)
4952 /* get column width from header */
4953 ZeroMemory(&hdi, sizeof(hdi));
4954 hdi.mask = HDI_WIDTH;
4955 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4956 nColumnWidth = hdi.cxy;
4959 return nColumnWidth;
4962 /***
4963 * DESCRIPTION:
4964 * In list or report display mode, retrieves the number of items that can fit
4965 * vertically in the visible area. In icon or small icon display mode,
4966 * retrieves the total number of visible items.
4968 * PARAMETER(S):
4969 * [I] infoPtr : valid pointer to the listview structure
4971 * RETURN:
4972 * Number of fully visible items.
4974 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4976 UINT uView = get_listview_type(infoPtr);
4977 INT nItemCount = 0;
4979 if (uView == LVS_LIST)
4981 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4983 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4984 LISTVIEW_GetCountPerColumn(infoPtr);
4987 else if (uView == LVS_REPORT)
4989 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4991 else
4993 nItemCount = GETITEMCOUNT(infoPtr);
4996 return nItemCount;
5000 /***
5001 * DESCRIPTION:
5002 * Retrieves an image list handle.
5004 * PARAMETER(S):
5005 * [I] infoPtr : valid pointer to the listview structure
5006 * [I] INT : image list identifier
5008 * RETURN:
5009 * SUCCESS : image list handle
5010 * FAILURE : NULL
5012 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5014 HIMAGELIST himl = NULL;
5016 switch (nImageList)
5018 case LVSIL_NORMAL:
5019 himl = infoPtr->himlNormal;
5020 break;
5021 case LVSIL_SMALL:
5022 himl = infoPtr->himlSmall;
5023 break;
5024 case LVSIL_STATE:
5025 himl = infoPtr->himlState;
5026 break;
5029 return (LRESULT)himl;
5032 /* LISTVIEW_GetISearchString */
5034 /***
5035 * DESCRIPTION:
5036 * Retrieves item attributes.
5038 * PARAMETER(S):
5039 * [I] hwnd : window handle
5040 * [IO] lpLVItem : item info
5041 * [I] internal : if true then we will use tricks that avoid copies
5042 * but are not compatible with the regular interface
5043 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5044 * if FALSE, the lpLVItem is a LPLVITEMA.
5046 * RETURN:
5047 * SUCCESS : TRUE
5048 * FAILURE : FALSE
5050 static LRESULT LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5052 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
5053 NMLVDISPINFOW dispInfo;
5054 LISTVIEW_SUBITEM *lpSubItem;
5055 LISTVIEW_ITEM *lpItem;
5056 HDPA hdpaSubItems;
5057 void* null = NULL;
5058 INT* piImage = (INT*)&null;
5059 LPWSTR* ppszText= (LPWSTR*)&null;
5060 LPARAM* plParam = (LPARAM*)&null;
5062 if (internal && !isW)
5064 ERR("We can't have internal non-Unicode GetItem!\n");
5065 return FALSE;
5068 /* In the following:
5069 * lpLVItem describes the information requested by the user
5070 * lpItem/lpSubItem is what we have
5071 * dispInfo is a structure we use to request the missing
5072 * information from the application
5075 TRACE("(lpLVItem=%s, internal=%d, isW=%d)\n",
5076 debuglvitem_t(lpLVItem, isW), internal, isW);
5078 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5079 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5080 return FALSE;
5082 ZeroMemory(&dispInfo, sizeof(dispInfo));
5084 if (lStyle & LVS_OWNERDATA)
5086 if (lpLVItem->mask & ~LVIF_STATE)
5088 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5089 dispinfo_notifyT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5090 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5091 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5094 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5096 lpLVItem->state = 0;
5097 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5098 lpLVItem->state |= LVIS_FOCUSED;
5099 if (LISTVIEW_IsSelected(infoPtr,lpLVItem->iItem))
5100 lpLVItem->state |= LVIS_SELECTED;
5103 return TRUE;
5106 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5107 if (hdpaSubItems == NULL) return FALSE;
5109 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5110 return FALSE;
5112 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5113 if (lpLVItem->iSubItem == 0)
5115 piImage=&lpItem->iImage;
5116 ppszText=&lpItem->pszText;
5117 plParam=&lpItem->lParam;
5118 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5120 dispInfo.item.mask |= LVIF_STATE;
5121 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5124 else
5126 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5127 if (lpSubItem != NULL)
5129 piImage=&lpSubItem->iImage;
5130 ppszText=&lpSubItem->pszText;
5134 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5136 dispInfo.item.mask |= LVIF_IMAGE;
5139 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5141 dispInfo.item.mask |= LVIF_TEXT;
5142 dispInfo.item.pszText = lpLVItem->pszText;
5143 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5144 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5145 *dispInfo.item.pszText = '\0';
5146 if (dispInfo.item.pszText && (*ppszText == NULL))
5147 *dispInfo.item.pszText = '\0';
5150 if (dispInfo.item.mask != 0)
5152 /* We don't have all the requested info, query the application */
5153 dispInfo.item.iItem = lpLVItem->iItem;
5154 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5155 dispInfo.item.lParam = lpItem->lParam;
5156 dispinfo_notifyT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5157 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5160 if (dispInfo.item.mask & LVIF_IMAGE)
5162 lpLVItem->iImage = dispInfo.item.iImage;
5163 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5164 *piImage = dispInfo.item.iImage;
5166 else if (lpLVItem->mask & LVIF_IMAGE)
5168 lpLVItem->iImage = *piImage;
5171 if (dispInfo.item.mask & LVIF_PARAM)
5173 lpLVItem->lParam = dispInfo.item.lParam;
5174 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5175 *plParam = dispInfo.item.lParam;
5177 else if (lpLVItem->mask & LVIF_PARAM)
5178 lpLVItem->lParam = lpItem->lParam;
5180 if (dispInfo.item.mask & LVIF_TEXT)
5182 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5183 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5185 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5186 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5187 if (lpLVItem->pszText != dispInfo.item.pszText)
5188 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5191 else if (lpLVItem->mask & LVIF_TEXT)
5193 if (internal) lpLVItem->pszText = *ppszText;
5194 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5197 if (lpLVItem->iSubItem == 0)
5199 if (dispInfo.item.mask & LVIF_STATE)
5201 lpLVItem->state = lpItem->state;
5202 lpLVItem->state &= ~dispInfo.item.stateMask;
5203 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5205 lpLVItem->state &= ~LVIS_SELECTED;
5206 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5207 LISTVIEW_IsSelected(infoPtr,dispInfo.item.iItem))
5208 lpLVItem->state |= LVIS_SELECTED;
5210 else if (lpLVItem->mask & LVIF_STATE)
5212 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5214 lpLVItem->state &= ~LVIS_SELECTED;
5215 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5216 LISTVIEW_IsSelected(infoPtr,lpLVItem->iItem))
5217 lpLVItem->state |= LVIS_SELECTED;
5220 if (lpLVItem->mask & LVIF_PARAM)
5221 lpLVItem->lParam = lpItem->lParam;
5223 if (lpLVItem->mask & LVIF_INDENT)
5224 lpLVItem->iIndent = lpItem->iIndent;
5227 return TRUE;
5231 /***
5232 * DESCRIPTION:
5233 * Retrieves the rectangle enclosing the item icon and text.
5235 * PARAMETER(S):
5236 * [I] infoPtr : valid pointer to the listview structure
5237 * [I] INT : item index
5238 * [O] LPRECT : coordinate information
5240 * RETURN:
5241 * SUCCESS : TRUE
5242 * FAILURE : FALSE
5244 static BOOL LISTVIEW_GetItemBoundBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lpRect)
5246 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
5247 UINT uView = lStyle & LVS_TYPEMASK;
5248 BOOL bResult = FALSE;
5249 HDPA hdpaSubItems;
5250 LISTVIEW_ITEM *lpItem;
5251 INT nCountPerColumn;
5252 INT nRow;
5254 TRACE("(nItem=%d,lpRect=%p)\n", nItem, lpRect);
5256 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5257 (lpRect != NULL))
5259 if (uView == LVS_LIST)
5261 bResult = TRUE;
5262 nItem = nItem - ListView_GetTopIndex(infoPtr->hwndSelf);
5263 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5264 if (nItem < 0)
5266 nRow = nItem % nCountPerColumn;
5267 if (nRow == 0)
5269 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5270 lpRect->top = 0;
5272 else
5274 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5275 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5278 else
5280 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5281 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5284 else if (uView == LVS_REPORT)
5286 bResult = TRUE;
5287 lpRect->left = REPORT_MARGINX;
5288 lpRect->top = ((nItem - ListView_GetTopIndex(infoPtr->hwndSelf)) *
5289 infoPtr->nItemHeight) + infoPtr->rcList.top;
5291 if (!(lStyle & LVS_NOSCROLL))
5293 SCROLLINFO scrollInfo;
5294 /* Adjust position by scrollbar offset */
5295 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5296 scrollInfo.cbSize = sizeof(SCROLLINFO);
5297 scrollInfo.fMask = SIF_POS;
5298 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
5299 lpRect->left -= scrollInfo.nPos;
5302 else /* either LVS_ICON or LVS_SMALLICON */
5304 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5306 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5308 bResult = TRUE;
5309 lpRect->left = lpItem->ptPosition.x;
5310 lpRect->top = lpItem->ptPosition.y;
5315 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5316 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5317 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5318 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5319 return bResult;
5322 /***
5323 * DESCRIPTION:
5324 * Retrieves the position (upper-left) of the listview control item.
5325 * Note that for LVS_ICON style, the upper-left is that of the icon
5326 * and not the bounding box.
5328 * PARAMETER(S):
5329 * [I] infoPtr : valid pointer to the listview structure
5330 * [I] INT : item index
5331 * [O] LPPOINT : coordinate information
5333 * RETURN:
5334 * SUCCESS : TRUE
5335 * FAILURE : FALSE
5337 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5339 UINT uView = get_listview_type(infoPtr);
5340 BOOL bResult = FALSE;
5341 RECT rcBounding;
5343 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5345 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5346 (lpptPosition != NULL))
5348 bResult = LISTVIEW_GetItemBoundBox(infoPtr, nItem, &rcBounding);
5349 lpptPosition->x = rcBounding.left;
5350 lpptPosition->y = rcBounding.top;
5351 if (uView == LVS_ICON)
5353 lpptPosition->y += ICON_TOP_PADDING;
5354 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5356 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5357 lpptPosition->x, lpptPosition->y);
5359 return bResult;
5362 /***
5363 * Update the bounding rectangle around the text under a large icon.
5364 * This depends on whether it has the focus or not.
5365 * On entry the rectangle's top, left and right should be set.
5366 * On return the bottom will also be set and the width may have been
5367 * modified.
5369 * PARAMETER
5370 * [I] infoPtr : pointer to the listview structure
5371 * [I] nItem : the item for which we are calculating this
5372 * [I] rect : the rectangle to be updated
5374 * This appears to be weird, even in the Microsoft implementation.
5376 static void LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *infoPtr, int nItem, RECT *rect)
5378 HDC hdc = GetDC (infoPtr->hwndSelf);
5379 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5381 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5383 /* We (aim to) display the full text. In Windows 95 it appears to
5384 * calculate the size assuming the specified font and then it draws
5385 * the text in that region with the specified font except scaled to
5386 * 10 point (or the height of the system font or ...). Thus if the
5387 * window has 24 point Helvetica the highlit rectangle will be
5388 * taller than the text and if it is 7 point Helvetica then the text
5389 * will be clipped.
5390 * For now we will simply say that it is the correct size to display
5391 * the text in the specified font.
5393 LVITEMW lvItem;
5394 lvItem.mask = LVIF_TEXT;
5395 lvItem.iItem = nItem;
5396 lvItem.iSubItem = 0;
5397 /* We will specify INTERNAL and so will receive back a const
5398 * pointer to the text, rather than specifying a buffer to which
5399 * to copy it.
5401 LISTVIEW_GetItemW (infoPtr, &lvItem, TRUE);
5402 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5403 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5404 DT_WORDBREAK | DT_NOPREFIX);
5405 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5407 else
5409 /* As far as I can see the text region seems to be trying to be
5410 * "tall enough for two lines of text". Once again (comctl32.dll ver
5411 * 5.81?) it measures this on the basis of the selected font and then
5412 * draws it with the same font except in 10 point size. This can lead
5413 * to more or less than the two rows appearing.
5414 * Question; are we supposed to be including DT_EXTERNALLEADING?
5415 * Question; should the width be shrunk to the space required to
5416 * display the two lines?
5418 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5421 SelectObject (hdc, hOldFont);
5422 ReleaseDC (infoPtr->hwndSelf, hdc);
5425 /***
5426 * DESCRIPTION:
5427 * Retrieves the bounding rectangle for a listview control item.
5429 * PARAMETER(S):
5430 * [I] infoPtr : valid pointer to the listview structure
5431 * [I] INT : item index
5432 * [IO] LPRECT : bounding rectangle coordinates
5433 * lprc->left specifies the portion of the item for which the bounding
5434 * rectangle will be retrieved.
5436 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5437 * including the icon and label.
5438 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5439 * LVIR_LABEL Returns the bounding rectangle of the item text.
5440 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5441 * rectangles, but excludes columns in report view.
5443 * RETURN:
5444 * SUCCESS : TRUE
5445 * FAILURE : FALSE
5447 * NOTES
5448 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5449 * upon whether the window has the focus currently and on whether the item
5450 * is the one with the focus. Ensure that the control's record of which
5451 * item has the focus agrees with the items' records.
5453 static LRESULT LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5455 UINT uView = get_listview_type(infoPtr);
5456 BOOL bResult = FALSE;
5457 POINT ptOrigin;
5458 POINT ptItem;
5459 INT nLeftPos;
5460 INT nLabelWidth;
5461 INT nIndent;
5462 LVITEMW lvItem;
5463 RECT rcInternal;
5465 TRACE("(nItem=%d, lprc=%p)\n", nItem, lprc);
5467 if (uView & LVS_REPORT)
5469 ZeroMemory(&lvItem, sizeof(lvItem));
5470 lvItem.mask = LVIF_INDENT;
5471 lvItem.iItem = nItem;
5472 lvItem.iSubItem = 0;
5473 LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE);
5475 /* do indent */
5476 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5477 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5478 else
5479 nIndent = 0;
5481 else
5482 nIndent = 0;
5484 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5486 switch(lprc->left)
5488 case LVIR_ICON:
5489 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptItem)) break;
5490 if (uView == LVS_ICON)
5492 if (infoPtr->himlNormal != NULL)
5494 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5496 bResult = TRUE;
5497 lprc->left = ptItem.x + ptOrigin.x;
5498 lprc->top = ptItem.y + ptOrigin.y;
5499 lprc->right = lprc->left + infoPtr->iconSize.cx;
5500 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5501 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5505 else if (uView == LVS_SMALLICON)
5507 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5509 bResult = TRUE;
5510 lprc->left = ptItem.x + ptOrigin.x;
5511 lprc->top = ptItem.y + ptOrigin.y;
5512 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5514 if (infoPtr->himlState != NULL)
5515 lprc->left += infoPtr->iconSize.cx;
5517 if (infoPtr->himlSmall != NULL)
5518 lprc->right = lprc->left + infoPtr->iconSize.cx;
5519 else
5520 lprc->right = lprc->left;
5523 else
5525 bResult = TRUE;
5526 lprc->left = ptItem.x;
5527 if (uView & LVS_REPORT)
5528 lprc->left += nIndent;
5529 lprc->top = ptItem.y;
5530 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5532 if (infoPtr->himlState != NULL)
5533 lprc->left += infoPtr->iconSize.cx;
5535 if (infoPtr->himlSmall != NULL)
5536 lprc->right = lprc->left + infoPtr->iconSize.cx;
5537 else
5538 lprc->right = lprc->left;
5540 break;
5542 case LVIR_LABEL:
5543 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptItem)) break;
5544 if (uView == LVS_ICON)
5546 if (infoPtr->himlNormal != NULL)
5548 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5550 bResult = TRUE;
5551 lprc->left = ptItem.x + ptOrigin.x;
5552 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5553 ICON_BOTTOM_PADDING);
5554 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5555 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5557 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5558 lprc->right = lprc->left + nLabelWidth;
5560 else
5562 lprc->left += 1;
5563 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5564 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, lprc);
5566 lprc->bottom = lprc->top + infoPtr->ntmHeight + HEIGHT_PADDING;
5570 else if (uView == LVS_SMALLICON)
5572 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5574 bResult = TRUE;
5575 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5576 lprc->top = ptItem.y + ptOrigin.y;
5577 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5579 if (infoPtr->himlState != NULL)
5580 lprc->left += infoPtr->iconSize.cx;
5582 if (infoPtr->himlSmall != NULL)
5583 lprc->left += infoPtr->iconSize.cx;
5585 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5586 nLabelWidth += TRAILING_PADDING;
5587 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5588 lprc->right = lprc->left + nLabelWidth;
5589 else
5590 lprc->right = nLeftPos + infoPtr->nItemWidth;
5593 else
5595 bResult = TRUE;
5596 if (uView == LVS_REPORT)
5597 nLeftPos = lprc->left = ptItem.x + nIndent;
5598 else
5599 nLeftPos = lprc->left = ptItem.x;
5600 lprc->top = ptItem.y;
5601 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5603 if (infoPtr->himlState != NULL)
5604 lprc->left += infoPtr->iconSize.cx;
5606 if (infoPtr->himlSmall != NULL)
5607 lprc->left += infoPtr->iconSize.cx;
5609 if (uView != LVS_REPORT)
5611 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5612 nLabelWidth += TRAILING_PADDING;
5613 if (infoPtr->himlSmall)
5614 nLabelWidth += IMAGE_PADDING;
5616 else
5617 nLabelWidth = LISTVIEW_GetColumnWidth(infoPtr, 0)-lprc->left;
5618 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5619 lprc->right = lprc->left + nLabelWidth;
5620 else
5621 lprc->right = nLeftPos + infoPtr->nItemWidth;
5623 break;
5625 case LVIR_BOUNDS:
5626 if (!LISTVIEW_GetItemBoundBox(infoPtr, nItem, &rcInternal)) break;
5627 ptItem.x = rcInternal.left;
5628 ptItem.y = rcInternal.top;
5629 if (uView == LVS_ICON)
5631 if (infoPtr->himlNormal != NULL)
5633 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5635 RECT label_rect;
5636 INT text_left, text_right, icon_left, text_pos_x;
5637 /* for style LVS_ICON bounds
5638 * left = min(icon.left, text.left)
5639 * right = max(icon.right, text.right)
5640 * top = boundbox.top + NOTHITABLE
5641 * bottom = text.bottom + 1
5643 bResult = TRUE;
5644 icon_left = text_left = ptItem.x;
5646 /* Correct ptItem to icon upper-left */
5647 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5648 ptItem.y += ICON_TOP_PADDING;
5650 /* Compute the label left and right */
5651 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5652 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5653 if (text_pos_x > 1)
5655 text_left += text_pos_x / 2;
5656 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5658 else
5660 text_left += 1;
5661 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5664 /* Compute rectangle w/o the text height */
5665 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5666 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5667 text_right) + ptOrigin.x;
5668 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5669 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5670 + infoPtr->iconSize.cy + 1
5671 + ICON_BOTTOM_PADDING;
5673 CopyRect (&label_rect, lprc);
5674 label_rect.top = lprc->bottom;
5675 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, &label_rect);
5676 UnionRect (lprc, lprc, &label_rect);
5680 else if (uView == LVS_SMALLICON)
5682 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5684 bResult = TRUE;
5685 lprc->left = ptItem.x + ptOrigin.x;
5686 lprc->right = lprc->left;
5687 lprc->top = ptItem.y + ptOrigin.y;
5688 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5689 if (infoPtr->himlState != NULL)
5690 lprc->right += infoPtr->iconSize.cx;
5691 if (infoPtr->himlSmall != NULL)
5692 lprc->right += infoPtr->iconSize.cx;
5694 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5695 nLabelWidth += TRAILING_PADDING;
5696 if (infoPtr->himlSmall)
5697 nLabelWidth += IMAGE_PADDING;
5698 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5699 lprc->right += nLabelWidth;
5700 else
5701 lprc->right = lprc->left + infoPtr->nItemWidth;
5704 else
5706 bResult = TRUE;
5707 lprc->left = ptItem.x;
5708 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5709 lprc->left += nIndent;
5710 lprc->right = lprc->left;
5711 lprc->top = ptItem.y;
5712 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5714 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5716 RECT br;
5717 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5718 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5720 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5722 else
5724 if (infoPtr->himlState != NULL)
5725 lprc->right += infoPtr->iconSize.cx;
5727 if (infoPtr->himlSmall != NULL)
5728 lprc->right += infoPtr->iconSize.cx;
5730 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5731 nLabelWidth += TRAILING_PADDING;
5732 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5733 lprc->right += nLabelWidth;
5734 else
5735 lprc->right = lprc->left + infoPtr->nItemWidth;
5738 break;
5740 case LVIR_SELECTBOUNDS:
5741 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptItem)) break;
5742 if (uView == LVS_ICON)
5744 if (infoPtr->himlNormal != NULL)
5746 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5748 bResult = TRUE;
5749 lprc->left = ptItem.x + ptOrigin.x;
5750 lprc->top = ptItem.y + ptOrigin.y;
5751 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5752 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5756 else if (uView == LVS_SMALLICON)
5758 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5760 bResult = TRUE;
5761 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5762 lprc->top = ptItem.y + ptOrigin.y;
5763 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5765 if (infoPtr->himlState != NULL)
5766 lprc->left += infoPtr->iconSize.cx;
5768 lprc->right = lprc->left;
5770 if (infoPtr->himlSmall != NULL)
5771 lprc->right += infoPtr->iconSize.cx;
5773 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5774 nLabelWidth += TRAILING_PADDING;
5775 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5776 lprc->right += nLabelWidth;
5777 else
5778 lprc->right = nLeftPos + infoPtr->nItemWidth;
5781 else
5783 bResult = TRUE;
5784 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5785 nLeftPos = lprc->left = ptItem.x + nIndent;
5786 else
5787 nLeftPos = lprc->left = ptItem.x;
5788 lprc->top = ptItem.y;
5789 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5791 if (infoPtr->himlState != NULL)
5792 lprc->left += infoPtr->iconSize.cx;
5794 lprc->right = lprc->left;
5796 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5798 RECT br;
5799 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5800 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5802 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5804 else
5806 if (infoPtr->himlSmall != NULL)
5807 lprc->right += infoPtr->iconSize.cx;
5809 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5810 nLabelWidth += TRAILING_PADDING;
5811 if (infoPtr->himlSmall)
5812 nLabelWidth += IMAGE_PADDING;
5813 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5814 lprc->right += nLabelWidth;
5815 else
5816 lprc->right = nLeftPos + infoPtr->nItemWidth;
5819 break;
5821 TRACE("result %s (%d,%d)-(%d,%d)\n",
5822 (bResult) ? "TRUE" : "FALSE",
5823 lprc->left, lprc->top, lprc->right, lprc->bottom);
5826 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5827 lprc->left, lprc->top, lprc->right, lprc->bottom);
5829 return bResult;
5833 static LRESULT LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem, INT
5834 flags, LPRECT lprc)
5836 UINT uView = get_listview_type(infoPtr);
5837 INT count;
5839 TRACE("(nItem=%d, nSubItem=%d lprc=%p)\n", nItem, nSubItem,
5840 lprc);
5842 if (!(uView & LVS_REPORT))
5843 return FALSE;
5845 if (flags & LVIR_ICON)
5847 FIXME("Unimplemented LVIR_ICON\n");
5848 return FALSE;
5850 else
5852 int top = min(infoPtr->nColumnCount, nSubItem - 1);
5854 LISTVIEW_GetItemRect(infoPtr,nItem,lprc);
5855 for (count = 0; count < top; count++)
5856 lprc->left += LISTVIEW_GetColumnWidth(infoPtr,count);
5858 lprc->right = LISTVIEW_GetColumnWidth(infoPtr,(nSubItem-1)) +
5859 lprc->left;
5861 return TRUE;
5865 /***
5866 * DESCRIPTION:
5867 * Retrieves the width of a label.
5869 * PARAMETER(S):
5870 * [I] infoPtr : valid pointer to the listview structure
5872 * RETURN:
5873 * SUCCESS : string width (in pixels)
5874 * FAILURE : zero
5876 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5878 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5879 INT nLabelWidth = 0;
5880 LVITEMW lvItem;
5882 TRACE("(nItem=%d)\n", nItem);
5884 ZeroMemory(&lvItem, sizeof(lvItem));
5885 lvItem.mask = LVIF_TEXT;
5886 lvItem.iItem = nItem;
5887 lvItem.cchTextMax = DISP_TEXT_SIZE;
5888 lvItem.pszText = szDispText;
5889 if (LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE))
5890 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5892 return nLabelWidth;
5895 /***
5896 * DESCRIPTION:
5897 * Retrieves the spacing between listview control items.
5899 * PARAMETER(S):
5900 * [I] infoPtr : valid pointer to the listview structure
5901 * [I] BOOL : flag for small or large icon
5903 * RETURN:
5904 * Horizontal + vertical spacing
5906 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5908 LONG lResult;
5910 if (!bSmall)
5912 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5914 else
5916 if (get_listview_type(infoPtr) == LVS_ICON)
5917 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5918 else
5919 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5921 return lResult;
5924 /***
5925 * DESCRIPTION:
5926 * Retrieves the state of a listview control item.
5928 * PARAMETER(S):
5929 * [I] infoPtr : valid pointer to the listview structure
5930 * [I] INT : item index
5931 * [I] UINT : state mask
5933 * RETURN:
5934 * State specified by the mask.
5936 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5938 LVITEMW lvItem;
5939 UINT uState = 0;
5941 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5943 ZeroMemory(&lvItem, sizeof(lvItem));
5944 lvItem.iItem = nItem;
5945 lvItem.stateMask = uMask;
5946 lvItem.mask = LVIF_STATE;
5947 if (LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE))
5948 uState = lvItem.state;
5951 return uState;
5954 /***
5955 * DESCRIPTION:
5956 * Retrieves the text of a listview control item or subitem.
5958 * PARAMETER(S):
5959 * [I] hwnd : window handle
5960 * [I] nItem : item index
5961 * [IO] lpLVItem : item information
5962 * [I] isW : TRUE if lpLVItem is Unicode
5964 * RETURN:
5965 * SUCCESS : string length
5966 * FAILURE : 0
5968 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5970 INT nLength = 0;
5972 if (lpLVItem != NULL)
5974 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5976 lpLVItem->mask = LVIF_TEXT;
5977 lpLVItem->iItem = nItem;
5978 if (LISTVIEW_GetItemT(infoPtr, lpLVItem, FALSE, isW))
5979 nLength = textlenT(lpLVItem->pszText, isW);
5983 return nLength;
5986 /***
5987 * DESCRIPTION:
5988 * Searches for an item based on properties + relationships.
5990 * PARAMETER(S):
5991 * [I] infoPtr : valid pointer to the listview structure
5992 * [I] INT : item index
5993 * [I] INT : relationship flag
5995 * RETURN:
5996 * SUCCESS : item index
5997 * FAILURE : -1
5999 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6001 UINT uView = get_listview_type(infoPtr);
6002 UINT uMask = 0;
6003 LVFINDINFOW lvFindInfo;
6004 INT nCountPerColumn;
6005 INT i;
6007 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6009 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6011 if (uFlags & LVNI_CUT)
6012 uMask |= LVIS_CUT;
6014 if (uFlags & LVNI_DROPHILITED)
6015 uMask |= LVIS_DROPHILITED;
6017 if (uFlags & LVNI_FOCUSED)
6018 uMask |= LVIS_FOCUSED;
6020 if (uFlags & LVNI_SELECTED)
6021 uMask |= LVIS_SELECTED;
6023 if (uFlags & LVNI_ABOVE)
6025 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6027 while (nItem >= 0)
6029 nItem--;
6030 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6031 return nItem;
6034 else
6036 lvFindInfo.flags = LVFI_NEARESTXY;
6037 lvFindInfo.vkDirection = VK_UP;
6038 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
6039 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6041 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6042 return nItem;
6046 else if (uFlags & LVNI_BELOW)
6048 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6050 while (nItem < GETITEMCOUNT(infoPtr))
6052 nItem++;
6053 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6054 return nItem;
6057 else
6059 lvFindInfo.flags = LVFI_NEARESTXY;
6060 lvFindInfo.vkDirection = VK_DOWN;
6061 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
6062 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6064 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6065 return nItem;
6069 else if (uFlags & LVNI_TOLEFT)
6071 if (uView == LVS_LIST)
6073 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6074 while (nItem - nCountPerColumn >= 0)
6076 nItem -= nCountPerColumn;
6077 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6078 return nItem;
6081 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6083 lvFindInfo.flags = LVFI_NEARESTXY;
6084 lvFindInfo.vkDirection = VK_LEFT;
6085 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
6086 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6088 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6089 return nItem;
6093 else if (uFlags & LVNI_TORIGHT)
6095 if (uView == LVS_LIST)
6097 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6098 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6100 nItem += nCountPerColumn;
6101 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6102 return nItem;
6105 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6107 lvFindInfo.flags = LVFI_NEARESTXY;
6108 lvFindInfo.vkDirection = VK_RIGHT;
6109 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
6110 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6112 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6113 return nItem;
6117 else
6119 nItem++;
6121 /* search by index */
6122 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6124 if ((ListView_GetItemState(infoPtr->hwndSelf, i, uMask) & uMask) == uMask)
6125 return i;
6130 return -1;
6133 /* LISTVIEW_GetNumberOfWorkAreas */
6135 /***
6136 * DESCRIPTION:
6137 * Retrieves the origin coordinates when in icon or small icon display mode.
6139 * PARAMETER(S):
6140 * [I] infoPtr : valid pointer to the listview structure
6141 * [O] LPPOINT : coordinate information
6143 * RETURN:
6144 * SUCCESS : TRUE
6145 * FAILURE : FALSE
6147 static LRESULT LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6149 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
6150 UINT uView = lStyle & LVS_TYPEMASK;
6151 BOOL bResult = FALSE;
6153 TRACE("(lpptOrigin=%p)\n", lpptOrigin);
6155 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6157 SCROLLINFO scrollInfo;
6158 ZeroMemory(lpptOrigin, sizeof(POINT));
6159 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6160 scrollInfo.cbSize = sizeof(SCROLLINFO);
6162 if (lStyle & WS_HSCROLL)
6164 scrollInfo.fMask = SIF_POS;
6165 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6166 lpptOrigin->x = -scrollInfo.nPos;
6169 if (lStyle & WS_VSCROLL)
6171 scrollInfo.fMask = SIF_POS;
6172 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6173 lpptOrigin->y = -scrollInfo.nPos;
6176 bResult = TRUE;
6178 TRACE("(pt=(%ld,%ld))\n", lpptOrigin->x, lpptOrigin->y);
6182 return bResult;
6185 /***
6186 * DESCRIPTION:
6187 * Retrieves the number of items that are marked as selected.
6189 * PARAMETER(S):
6190 * [I] infoPtr : valid pointer to the listview structure
6192 * RETURN:
6193 * Number of items selected.
6195 static LRESULT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
6197 /* REDO THIS */
6198 INT nSelectedCount = 0;
6199 INT i;
6201 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6203 if (ListView_GetItemState(infoPtr->hwndSelf, i, LVIS_SELECTED) & LVIS_SELECTED)
6204 nSelectedCount++;
6207 return nSelectedCount;
6210 /***
6211 * DESCRIPTION:
6212 * Retrieves the width of a string.
6214 * PARAMETER(S):
6215 * [I] hwnd : window handle
6216 * [I] lpszText : text string to process
6217 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6219 * RETURN:
6220 * SUCCESS : string width (in pixels)
6221 * FAILURE : zero
6223 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6225 if (is_textT(lpszText, isW))
6227 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6228 HDC hdc = GetDC(infoPtr->hwndSelf);
6229 HFONT hOldFont = SelectObject(hdc, hFont);
6230 SIZE stringSize;
6231 ZeroMemory(&stringSize, sizeof(SIZE));
6232 if (isW)
6233 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6234 else
6235 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6236 SelectObject(hdc, hOldFont);
6237 ReleaseDC(infoPtr->hwndSelf, hdc);
6238 return stringSize.cx;
6240 return 0;
6243 /***
6244 * DESCRIPTION:
6245 * Retrieves the text backgound color.
6247 * PARAMETER(S):
6248 * [I] infoPtr : valid pointer to the listview structure
6250 * RETURN:
6251 * COLORREF associated with the the background.
6253 static LRESULT LISTVIEW_GetTextBkColor(LISTVIEW_INFO *infoPtr)
6255 return infoPtr->clrTextBk;
6258 /***
6259 * DESCRIPTION:
6260 * Retrieves the text color.
6262 * PARAMETER(S):
6263 * [I] infoPtr : valid pointer to the listview structure
6265 * RETURN:
6266 * COLORREF associated with the text.
6268 static LRESULT LISTVIEW_GetTextColor(LISTVIEW_INFO *infoPtr)
6270 return infoPtr->clrText;
6273 /***
6274 * DESCRIPTION:
6275 * Determines item if a hit or closest if not
6277 * PARAMETER(S):
6278 * [I] infoPtr : valid pointer to the listview structure
6279 * [IO] LPLV_INTHIT : hit test information
6280 * [I] subitem : fill out iSubItem.
6282 * RETURN:
6283 * SUCCESS : item index of hit
6284 * FAILURE : -1
6286 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *infoPtr, LPLV_INTHIT lpInt, BOOL subitem)
6288 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
6289 UINT uView = lStyle & LVS_TYPEMASK;
6290 INT i,j,topindex,bottomindex;
6291 RECT rcItem,rcSubItem;
6292 DWORD xterm, yterm, dist;
6294 TRACE("(x=%ld, y=%ld)\n", lpInt->ht.pt.x, lpInt->ht.pt.y);
6296 topindex = LISTVIEW_GetTopIndex(infoPtr);
6297 if (uView == LVS_REPORT)
6299 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6300 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6302 else
6304 bottomindex = GETITEMCOUNT(infoPtr);
6307 lpInt->distance = 0x7fffffff;
6308 lpInt->iDistItem = -1;
6310 for (i = topindex; i < bottomindex; i++)
6312 rcItem.left = LVIR_BOUNDS;
6313 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
6315 if (PtInRect(&rcItem, lpInt->ht.pt))
6317 rcSubItem = rcItem;
6318 rcItem.left = LVIR_ICON;
6319 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
6321 if (PtInRect(&rcItem, lpInt->ht.pt))
6323 lpInt->ht.flags = LVHT_ONITEMICON;
6324 lpInt->ht.iItem = i;
6325 goto set_subitem;
6329 rcItem.left = LVIR_LABEL;
6330 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
6332 if (PtInRect(&rcItem, lpInt->ht.pt))
6334 lpInt->ht.flags = LVHT_ONITEMLABEL;
6335 lpInt->ht.iItem = i;
6336 goto set_subitem;
6340 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6341 lpInt->ht.iItem = i;
6342 set_subitem:
6343 if (subitem)
6345 lpInt->ht.iSubItem = 0;
6346 rcSubItem.right = rcSubItem.left;
6347 for (j = 0; j < infoPtr->nColumnCount; j++)
6349 rcSubItem.left = rcSubItem.right;
6350 rcSubItem.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6351 if (PtInRect(&rcSubItem, lpInt->ht.pt))
6353 lpInt->ht.iSubItem = j;
6354 break;
6358 return i;
6360 else
6363 * Now compute distance from point to center of boundary
6364 * box. Since we are only interested in the relative
6365 * distance, we can skip the nasty square root operation
6367 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6368 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6369 dist = xterm * xterm + yterm * yterm;
6370 if (dist < lpInt->distance)
6372 lpInt->distance = dist;
6373 lpInt->iDistItem = i;
6379 lpInt->ht.flags = LVHT_NOWHERE;
6380 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6382 return -1;
6385 /***
6386 * DESCRIPTION:
6387 * Determines which section of the item was selected (if any).
6389 * PARAMETER(S):
6390 * [I] infoPtr : valid pointer to the listview structure
6391 * [IO] LPLVHITTESTINFO : hit test information
6392 * [I] subitem : fill out iSubItem.
6394 * RETURN:
6395 * SUCCESS : item index
6396 * FAILURE : -1
6398 static INT LISTVIEW_HitTestItem(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6400 INT ret;
6401 LV_INTHIT lv_inthit;
6403 TRACE("(x=%ld, y=%ld)\n", lpHitTestInfo->pt.x,
6404 lpHitTestInfo->pt.y);
6406 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6407 ret = LISTVIEW_SuperHitTestItem(infoPtr, &lv_inthit, subitem);
6408 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6409 return ret;
6412 /***
6413 * DESCRIPTION:
6414 * Determines which listview item is located at the specified position.
6416 * PARAMETER(S):
6417 * [I] infoPtr : valid pointer to the listview structure
6418 * [IO} LPLVHITTESTINFO : hit test information
6420 * RETURN:
6421 * SUCCESS : item index
6422 * FAILURE : -1
6424 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpHitTestInfo)
6426 INT nItem = -1;
6428 lpHitTestInfo->flags = 0;
6430 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6431 lpHitTestInfo->flags = LVHT_TOLEFT;
6432 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6433 lpHitTestInfo->flags = LVHT_TORIGHT;
6434 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6435 lpHitTestInfo->flags |= LVHT_ABOVE;
6436 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6437 lpHitTestInfo->flags |= LVHT_BELOW;
6439 if (lpHitTestInfo->flags == 0)
6441 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6442 * an app might pass only a structure with space up to iItem!
6443 * (MS Office 97 does that for instance in the file open dialog)
6445 nItem = LISTVIEW_HitTestItem(infoPtr, lpHitTestInfo, FALSE);
6448 return nItem;
6451 /***
6452 * DESCRIPTION:
6453 * Determines which listview subitem is located at the specified position.
6455 * PARAMETER(S):
6456 * [I] infoPtr : valid pointer to the listview structure
6457 * [IO} LPLVHITTESTINFO : hit test information
6459 * RETURN:
6460 * SUCCESS : item index
6461 * FAILURE : -1
6463 static LRESULT LISTVIEW_SubItemHitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpHitTestInfo)
6465 INT nItem = -1;
6467 lpHitTestInfo->flags = 0;
6469 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6470 lpHitTestInfo->flags = LVHT_TOLEFT;
6471 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6472 lpHitTestInfo->flags = LVHT_TORIGHT;
6473 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6474 lpHitTestInfo->flags |= LVHT_ABOVE;
6475 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6476 lpHitTestInfo->flags |= LVHT_BELOW;
6478 if (lpHitTestInfo->flags == 0)
6479 nItem = LISTVIEW_HitTestItem(infoPtr, lpHitTestInfo, TRUE);
6481 return nItem;
6484 /***
6485 * DESCRIPTION:
6486 * Inserts a new column.
6488 * PARAMETER(S):
6489 * [I] infoPtr : valid pointer to the listview structure
6490 * [I] INT : column index
6491 * [I] LPLVCOLUMNW : column information
6493 * RETURN:
6494 * SUCCESS : new column index
6495 * FAILURE : -1
6497 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6498 LPLVCOLUMNW lpColumn, BOOL isW)
6500 INT nNewColumn = -1;
6501 HDITEMW hdi;
6503 TRACE("(nColumn=%d, lpColumn=%p)\n", nColumn, lpColumn);
6505 if (lpColumn != NULL)
6507 /* initialize memory */
6508 ZeroMemory(&hdi, sizeof(hdi));
6510 if (lpColumn->mask & LVCF_FMT)
6512 /* format member is valid */
6513 hdi.mask |= HDI_FORMAT;
6515 /* set text alignment (leftmost column must be left-aligned) */
6516 if (nColumn == 0)
6518 hdi.fmt |= HDF_LEFT;
6520 else
6522 if (lpColumn->fmt & LVCFMT_LEFT)
6524 hdi.fmt |= HDF_LEFT;
6526 else if (lpColumn->fmt & LVCFMT_RIGHT)
6528 hdi.fmt |= HDF_RIGHT;
6530 else if (lpColumn->fmt & LVCFMT_CENTER)
6532 hdi.fmt |= HDF_CENTER;
6536 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6538 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6539 /* ??? */
6542 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6544 /* ??? */
6547 if (lpColumn->fmt & LVCFMT_IMAGE)
6549 hdi.fmt |= HDF_IMAGE;
6550 hdi.iImage = I_IMAGECALLBACK;
6554 if (lpColumn->mask & LVCF_WIDTH)
6556 hdi.mask |= HDI_WIDTH;
6557 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6559 /* make it fill the remainder of the controls width */
6560 HDITEMW hdit;
6561 RECT rcHeader;
6562 INT item_index;
6564 ZeroMemory(&hdit, sizeof(hdit));
6566 /* get the width of every item except the current one */
6567 hdit.mask = HDI_WIDTH;
6568 hdi.cxy = 0;
6570 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6571 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6572 hdi.cxy+=hdit.cxy;
6575 /* retrieve the layout of the header */
6576 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6577 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6578 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6580 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6582 else
6583 hdi.cxy = lpColumn->cx;
6586 if (lpColumn->mask & LVCF_TEXT)
6588 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6589 hdi.pszText = lpColumn->pszText;
6590 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6591 hdi.fmt |= HDF_STRING;
6594 if (lpColumn->mask & LVCF_IMAGE)
6596 hdi.mask |= HDI_IMAGE;
6597 hdi.iImage = lpColumn->iImage;
6600 if (lpColumn->mask & LVCF_ORDER)
6602 hdi.mask |= HDI_ORDER;
6603 hdi.iOrder = lpColumn->iOrder;
6606 /* insert item in header control */
6607 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6608 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6609 (WPARAM)nColumn, (LPARAM)&hdi);
6611 /* Need to reset the item width when inserting a new column */
6612 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
6614 LISTVIEW_UpdateScroll(infoPtr);
6615 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
6618 return nNewColumn;
6621 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6622 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6623 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6624 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6625 their own sort proc. when sending LVM_SORTITEMS.
6627 /* Platform SDK:
6628 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6630 LVS_SORTXXX must be specified,
6631 LVS_OWNERDRAW is not set,
6632 <item>.pszText is not LPSTR_TEXTCALLBACK.
6634 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6635 are sorted based on item text..."
6637 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6639 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6640 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6641 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6642 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6643 /* if we're sorting descending, negate the return value */
6644 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6647 /***
6648 * nESCRIPTION:
6649 * Inserts a new item in the listview control.
6651 * PARAMETER(S):
6652 * [I] infoPtr : valid pointer to the listview structure
6653 * [I] LPLVITEMW : item information
6654 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6656 * RETURN:
6657 * SUCCESS : new item index
6658 * FAILURE : -1
6660 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6662 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
6663 UINT uView = lStyle & LVS_TYPEMASK;
6664 INT nItem = -1;
6665 HDPA hdpaSubItems;
6666 INT nItemWidth = 0;
6667 LISTVIEW_ITEM *lpItem = NULL;
6669 TRACE("(lpLVItem=%s, isW=%d)\n",
6670 debuglvitem_t(lpLVItem, isW), isW);
6672 if (lStyle & LVS_OWNERDATA)
6674 nItem = infoPtr->hdpaItems->nItemCount;
6675 infoPtr->hdpaItems->nItemCount ++;
6676 return nItem;
6679 if (lpLVItem != NULL)
6681 /* make sure it's not a subitem; cannot insert a subitem */
6682 if (lpLVItem->iSubItem == 0)
6684 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6686 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6687 if (LISTVIEW_InitItemT(infoPtr, lpItem, lpLVItem, isW))
6689 /* insert item in listview control data structure */
6690 if ( (hdpaSubItems = DPA_Create(8)) )
6692 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6694 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6695 && !(lStyle & LVS_OWNERDRAWFIXED)
6696 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6698 /* Insert the item in the proper sort order based on the pszText
6699 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6700 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6701 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6702 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr->hwndSelf );
6703 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6705 else
6707 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6708 hdpaSubItems);
6710 if (nItem != -1)
6712 NMLISTVIEW nmlv;
6714 LISTVIEW_ShiftIndices(infoPtr,nItem,1);
6716 /* manage item focus */
6717 if (lpLVItem->mask & LVIF_STATE)
6719 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6720 if (lpLVItem->stateMask & LVIS_SELECTED)
6721 LISTVIEW_SetSelection(infoPtr, nItem);
6722 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6723 LISTVIEW_SetItemFocus(infoPtr, nItem);
6726 /* send LVN_INSERTITEM notification */
6727 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6728 nmlv.iItem = nItem;
6729 nmlv.lParam = lpItem->lParam;
6730 listview_notify(infoPtr, LVN_INSERTITEM, &nmlv);
6732 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6734 nItemWidth = LISTVIEW_CalculateWidth(infoPtr, lpLVItem->iItem);
6735 if (nItemWidth > infoPtr->nItemWidth)
6736 infoPtr->nItemWidth = nItemWidth;
6739 /* align items (set position of each item) */
6740 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6742 if (lStyle & LVS_ALIGNLEFT)
6743 LISTVIEW_AlignLeft(infoPtr);
6744 else
6745 LISTVIEW_AlignTop(infoPtr);
6748 LISTVIEW_UpdateScroll(infoPtr);
6749 /* refresh client area */
6750 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
6759 /* free memory if unsuccessful */
6760 if ((nItem == -1) && (lpItem != NULL))
6761 COMCTL32_Free(lpItem);
6763 return nItem;
6766 /***
6767 * DESCRIPTION:
6768 * Redraws a range of items.
6770 * PARAMETER(S):
6771 * [I] infoPtr : valid pointer to the listview structure
6772 * [I] INT : first item
6773 * [I] INT : last item
6775 * RETURN:
6776 * SUCCESS : TRUE
6777 * FAILURE : FALSE
6779 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6781 RECT rcItem;
6782 INT i;
6784 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6785 max(nFirst, nLast) >= GETITEMCOUNT(infoPtr))
6786 return FALSE;
6788 for (i = nFirst; i <= nLast; i++)
6790 rcItem.left = LVIR_BOUNDS;
6791 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
6792 InvalidateRect(infoPtr->hwndSelf, &rcItem, TRUE);
6795 return TRUE;
6798 /***
6799 * DESCRIPTION:
6800 * Scroll the content of a listview.
6802 * PARAMETER(S):
6803 * [I] infoPtr : valid pointer to the listview structure
6804 * [I] INT : horizontal scroll amount in pixels
6805 * [I] INT : vertical scroll amount in pixels
6807 * RETURN:
6808 * SUCCESS : TRUE
6809 * FAILURE : FALSE
6811 * COMMENTS:
6812 * If the control is in report mode (LVS_REPORT) the control can
6813 * be scrolled only in line increments. "dy" will be rounded to the
6814 * nearest number of pixels that are a whole line. Ex: if line height
6815 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6816 * is passed the the scroll will be 0. (per MSDN 7/2002)
6818 * For: (per experimentaion with native control and CSpy ListView)
6819 * LVS_ICON dy=1 = 1 pixel (vertical only)
6820 * dx ignored
6821 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6822 * dx ignored
6823 * LVS_LIST dx=1 = 1 column (horizontal only)
6824 * but will only scroll 1 column per message
6825 * no matter what the value.
6826 * dy must be 0 or FALSE returned.
6827 * LVS_REPORT dx=1 = 1 pixel
6828 * dy= see above
6831 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6833 switch(get_listview_type(infoPtr)) {
6834 case LVS_REPORT:
6835 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6836 dy /= infoPtr->nItemHeight;
6837 break;
6838 case LVS_LIST:
6839 if (dy != 0) return FALSE;
6840 break;
6841 default: /* icon */
6842 dx = 0;
6843 break;
6846 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6847 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6849 return TRUE;
6852 /***
6853 * DESCRIPTION:
6854 * Sets the background color.
6856 * PARAMETER(S):
6857 * [I] infoPtr : valid pointer to the listview structure
6858 * [I] COLORREF : background color
6860 * RETURN:
6861 * SUCCESS : TRUE
6862 * FAILURE : FALSE
6864 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6866 if(infoPtr->clrBk != clrBk){
6867 infoPtr->clrBk = clrBk;
6868 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
6871 return TRUE;
6874 /* LISTVIEW_SetBkImage */
6876 /***
6877 * DESCRIPTION:
6878 * Sets the attributes of a header item.
6880 * PARAMETER(S):
6881 * [I] infoPtr : valid pointer to the listview structure
6882 * [I] INT : column index
6883 * [I] LPLVCOLUMNW : column attributes
6884 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6885 * otherwise it is in fact a LPLVCOLUMNA
6887 * RETURN:
6888 * SUCCESS : TRUE
6889 * FAILURE : FALSE
6891 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6892 LPLVCOLUMNW lpColumn, BOOL isW)
6894 BOOL bResult = FALSE;
6895 HDITEMW hdi, hdiget;
6897 if ((lpColumn != NULL) && (nColumn >= 0) &&
6898 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6900 /* initialize memory */
6901 ZeroMemory(&hdi, sizeof(hdi));
6903 if (lpColumn->mask & LVCF_FMT)
6905 /* format member is valid */
6906 hdi.mask |= HDI_FORMAT;
6908 /* get current format first */
6909 hdiget.mask = HDI_FORMAT;
6910 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6911 /* preserve HDF_STRING if present */
6912 hdi.fmt = hdiget.fmt & HDF_STRING;
6914 /* set text alignment (leftmost column must be left-aligned) */
6915 if (nColumn == 0)
6917 hdi.fmt |= HDF_LEFT;
6919 else
6921 if (lpColumn->fmt & LVCFMT_LEFT)
6922 hdi.fmt |= HDF_LEFT;
6923 else if (lpColumn->fmt & LVCFMT_RIGHT)
6924 hdi.fmt |= HDF_RIGHT;
6925 else if (lpColumn->fmt & LVCFMT_CENTER)
6926 hdi.fmt |= HDF_CENTER;
6929 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6930 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6932 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6933 hdi.fmt |= HDF_IMAGE;
6935 if (lpColumn->fmt & LVCFMT_IMAGE)
6937 hdi.fmt |= HDF_IMAGE;
6938 hdi.iImage = I_IMAGECALLBACK;
6942 if (lpColumn->mask & LVCF_WIDTH)
6944 hdi.mask |= HDI_WIDTH;
6945 hdi.cxy = lpColumn->cx;
6948 if (lpColumn->mask & LVCF_TEXT)
6950 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6951 hdi.pszText = lpColumn->pszText;
6952 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6953 hdi.fmt |= HDF_STRING;
6956 if (lpColumn->mask & LVCF_IMAGE)
6958 hdi.mask |= HDI_IMAGE;
6959 hdi.iImage = lpColumn->iImage;
6962 if (lpColumn->mask & LVCF_ORDER)
6964 hdi.mask |= HDI_ORDER;
6965 hdi.iOrder = lpColumn->iOrder;
6968 /* set header item attributes */
6969 if (isW)
6970 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6971 else
6972 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6975 return bResult;
6978 /***
6979 * DESCRIPTION:
6980 * Sets the column order array
6982 * PARAMETERS:
6983 * [I] infoPtr : valid pointer to the listview structure
6984 * [I] INT : number of elements in column order array
6985 * [I] INT : pointer to column order array
6987 * RETURN:
6988 * SUCCESS : TRUE
6989 * FAILURE : FALSE
6991 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6993 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6995 if (!lpiArray)
6996 return FALSE;
6998 return TRUE;
7002 /***
7003 * DESCRIPTION:
7004 * Sets the width of a column
7006 * PARAMETERS:
7007 * [I] infoPtr : valid pointer to the listview structure
7008 * [I] INT : column index
7009 * [I] INT : column width
7011 * RETURN:
7012 * SUCCESS : TRUE
7013 * FAILURE : FALSE
7015 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
7017 HDITEMW hdi;
7018 LRESULT lret;
7019 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7020 UINT uView = lStyle & LVS_TYPEMASK;
7021 HDC hdc;
7022 HFONT header_font;
7023 HFONT old_font;
7024 SIZE size;
7025 WCHAR text_buffer[DISP_TEXT_SIZE];
7026 INT header_item_count;
7027 INT item_index;
7028 INT nLabelWidth;
7029 RECT rcHeader;
7030 LVITEMW lvItem;
7031 WCHAR szDispText[DISP_TEXT_SIZE];
7033 if (!infoPtr->hwndHeader) /* make sure we have a header */
7034 return (FALSE);
7036 /* set column width only if in report or list mode */
7037 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7038 return (FALSE);
7040 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
7042 /* take care of invalid cx values */
7043 if((uView == LVS_REPORT) && (cx < -2))
7044 cx = LVSCW_AUTOSIZE;
7045 else if (uView == LVS_LIST && (cx < 1))
7046 return FALSE;
7048 /* resize all columns if in LVS_LIST mode */
7049 if(uView == LVS_LIST) {
7050 infoPtr->nItemWidth = cx;
7051 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); /* force redraw of the listview */
7052 return TRUE;
7055 /* autosize based on listview items width */
7056 if(cx == LVSCW_AUTOSIZE)
7058 /* set the width of the column to the width of the widest item */
7059 if (iCol == 0 || uView == LVS_LIST)
7061 cx = 0;
7062 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7064 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
7065 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7067 if (infoPtr->himlSmall)
7068 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7070 else
7072 ZeroMemory(&lvItem, sizeof(lvItem));
7073 lvItem.iSubItem = iCol;
7074 lvItem.mask = LVIF_TEXT;
7075 lvItem.cchTextMax = DISP_TEXT_SIZE;
7076 lvItem.pszText = szDispText;
7077 *lvItem.pszText = '\0';
7078 cx = 0;
7079 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7081 lvItem.iItem = item_index;
7082 LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, TRUE);
7083 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7084 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7087 cx += TRAILING_PADDING;
7088 } /* autosize based on listview header width */
7089 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7091 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7093 /* if iCol is the last column make it fill the remainder of the controls width */
7094 if(iCol == (header_item_count - 1)) {
7095 /* get the width of every item except the current one */
7096 hdi.mask = HDI_WIDTH;
7097 cx = 0;
7099 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7100 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7101 cx+=hdi.cxy;
7104 /* retrieve the layout of the header */
7105 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7107 cx = (rcHeader.right - rcHeader.left) - cx;
7109 else
7111 /* Despite what the MS docs say, if this is not the last
7112 column, then MS resizes the column to the width of the
7113 largest text string in the column, including headers
7114 and items. This is different from LVSCW_AUTOSIZE in that
7115 LVSCW_AUTOSIZE ignores the header string length.
7118 /* retrieve header font */
7119 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7121 /* retrieve header text */
7122 hdi.mask = HDI_TEXT;
7123 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7124 hdi.pszText = text_buffer;
7126 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7128 /* determine the width of the text in the header */
7129 hdc = GetDC(infoPtr->hwndSelf);
7130 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7132 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7134 SelectObject(hdc, old_font); /* restore the old font */
7135 ReleaseDC(infoPtr->hwndSelf, hdc);
7137 ZeroMemory(&lvItem, sizeof(lvItem));
7138 lvItem.iSubItem = iCol;
7139 lvItem.mask = LVIF_TEXT;
7140 lvItem.cchTextMax = DISP_TEXT_SIZE;
7141 lvItem.pszText = szDispText;
7142 *lvItem.pszText = '\0';
7143 cx = size.cx;
7144 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7146 lvItem.iItem = item_index;
7147 LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, TRUE);
7148 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7149 nLabelWidth += TRAILING_PADDING;
7150 /* While it is possible for subitems to have icons, even MS messes
7151 up the positioning, so I suspect no applications actually use
7152 them. */
7153 if (item_index == 0 && infoPtr->himlSmall)
7154 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7155 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7160 /* call header to update the column change */
7161 hdi.mask = HDI_WIDTH;
7163 hdi.cxy = cx;
7164 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7166 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); /* force redraw of the listview */
7168 return lret;
7171 /***
7172 * DESCRIPTION:
7173 * Sets the extended listview style.
7175 * PARAMETERS:
7176 * [I] infoPtr : valid pointer to the listview structure
7177 * [I] DWORD : mask
7178 * [I] DWORD : style
7180 * RETURN:
7181 * SUCCESS : previous style
7182 * FAILURE : 0
7184 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
7186 DWORD dwOldStyle = infoPtr->dwExStyle;
7188 /* set new style */
7189 if (dwMask)
7190 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7191 else
7192 infoPtr->dwExStyle = dwStyle;
7194 return dwOldStyle;
7197 /***
7198 * DESCRIPTION:
7199 * Sets the new hot cursor used during hot tracking and hover selection.
7201 * PARAMETER(S):
7202 * [I] infoPtr : valid pointer to the listview structure
7203 * [I} hCurosr : the new hot cursor handle
7205 * RETURN:
7206 * Returns the previous hot cursor
7208 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7210 HCURSOR oldCursor = infoPtr->hHotCursor;
7211 infoPtr->hHotCursor = hCursor;
7212 return oldCursor;
7216 /***
7217 * DESCRIPTION:
7218 * Sets the hot item index.
7220 * PARAMETERS:
7221 * [I] infoPtr : valid pointer to the listview structure
7222 * [I] INT : index
7224 * RETURN:
7225 * SUCCESS : previous hot item index
7226 * FAILURE : -1 (no hot item)
7228 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7230 INT iOldIndex = infoPtr->nHotItem;
7231 infoPtr->nHotItem = iIndex;
7232 return iOldIndex;
7236 /***
7237 * DESCRIPTION:
7238 * Sets the amount of time the cursor must hover over an item before it is selected.
7240 * PARAMETER(S):
7241 * [I] infoPtr : valid pointer to the listview structure
7242 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7244 * RETURN:
7245 * Returns the previous hover time
7247 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7249 DWORD oldHoverTime = infoPtr->dwHoverTime;
7250 infoPtr->dwHoverTime = dwHoverTime;
7251 return oldHoverTime;
7254 /***
7255 * DESCRIPTION:
7256 * Sets spacing for icons of LVS_ICON style.
7258 * PARAMETER(S):
7259 * [I] infoPtr : valid pointer to the listview structure
7260 * [I] DWORD : MAKELONG(cx, cy)
7262 * RETURN:
7263 * MAKELONG(oldcx, oldcy)
7265 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
7267 INT cy = HIWORD(spacing);
7268 INT cx = LOWORD(spacing);
7269 DWORD oldspacing;
7270 LONG lStyle = GetWindowLongA(infoPtr->hwndSelf, GWL_STYLE);
7271 UINT uView = lStyle & LVS_TYPEMASK;
7273 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7274 if (cx == -1) /* set to default */
7275 cx = GetSystemMetrics(SM_CXICONSPACING);
7276 if (cy == -1) /* set to default */
7277 cy = GetSystemMetrics(SM_CYICONSPACING);
7279 if (cx)
7280 infoPtr->iconSpacing.cx = cx;
7281 else
7282 { /* if 0 then compute width */
7283 if (uView == LVS_ICON)
7284 FIXME("width computation not yet done\n");
7286 * Should scan each item and determine max width of
7287 * icon or label, then make that the width
7289 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7290 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(infoPtr);
7292 if (cy)
7293 infoPtr->iconSpacing.cy = cy;
7294 else
7295 { /* if 0 then compute height */
7296 if (uView == LVS_ICON)
7297 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight
7298 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7299 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7300 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7301 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(infoPtr);
7304 TRACE("old=(%d,%d), new=(%ld,%ld), iconSize=(%ld,%ld), ntmH=%d\n",
7305 LOWORD(oldspacing), HIWORD(oldspacing),
7306 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy,
7307 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7308 infoPtr->ntmHeight);
7310 /* these depend on the iconSpacing */
7311 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
7312 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7314 return oldspacing;
7317 /***
7318 * DESCRIPTION:
7319 * Sets image lists.
7321 * PARAMETER(S):
7322 * [I] infoPtr : valid pointer to the listview structure
7323 * [I] INT : image list type
7324 * [I] HIMAGELIST : image list handle
7326 * RETURN:
7327 * SUCCESS : old image list
7328 * FAILURE : NULL
7330 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7332 HIMAGELIST himlOld = 0;
7333 INT oldHeight;
7334 UINT uView = get_listview_type(infoPtr);
7336 switch (nType)
7338 case LVSIL_NORMAL:
7339 himlOld = infoPtr->himlNormal;
7340 infoPtr->himlNormal = himl;
7341 if(himl && (LVS_ICON == uView))
7343 INT cx, cy;
7344 ImageList_GetIconSize(himl, &cx, &cy);
7345 TRACE("icon old size=(%ld,%ld), new size=(%d,%d)\n",
7346 infoPtr->iconSize.cx, infoPtr->iconSize.cy, cx, cy);
7347 infoPtr->iconSize.cx = cx;
7348 infoPtr->iconSize.cy = cy;
7349 LISTVIEW_SetIconSpacing(infoPtr,0);
7351 break;
7353 case LVSIL_SMALL:
7354 himlOld = infoPtr->himlSmall;
7355 infoPtr->himlSmall = himl;
7356 break;
7358 case LVSIL_STATE:
7359 himlOld = infoPtr->himlState;
7360 infoPtr->himlState = himl;
7361 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7362 break;
7365 oldHeight = infoPtr->nItemHeight;
7366 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7367 if (infoPtr->nItemHeight != oldHeight)
7368 LISTVIEW_UpdateScroll(infoPtr);
7370 return himlOld;
7373 /***
7374 * DESCRIPTION:
7375 * Preallocates memory (does *not* set the actual count of items !)
7377 * PARAMETER(S):
7378 * [I] infoPtr : valid pointer to the listview structure
7379 * [I] INT : item count (projected number of items to allocate)
7380 * [I] DWORD : update flags
7382 * RETURN:
7383 * SUCCESS : TRUE
7384 * FAILURE : FALSE
7386 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7388 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7390 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_OWNERDATA)
7392 int precount,topvisible;
7394 TRACE("LVS_OWNERDATA is set!\n");
7395 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7396 FIXME("flags %s %s not implemented\n",
7397 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7398 : "",
7399 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7402 * Internally remove all the selections.
7406 LISTVIEW_SELECTION *selection;
7407 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7408 if (selection)
7409 LISTVIEW_RemoveSelectionRange(infoPtr,selection->lower,
7410 selection->upper);
7412 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7414 precount = infoPtr->hdpaItems->nItemCount;
7415 topvisible = ListView_GetTopIndex(infoPtr->hwndSelf) +
7416 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
7418 infoPtr->hdpaItems->nItemCount = nItems;
7420 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(infoPtr),
7421 DEFAULT_COLUMN_WIDTH);
7423 LISTVIEW_UpdateSize(infoPtr);
7424 LISTVIEW_UpdateScroll(infoPtr);
7426 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7427 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7429 else
7431 /* According to MSDN for non-LVS_OWNERDATA this is just
7432 * a performance issue. The control allocates its internal
7433 * data structures for the number of items specified. It
7434 * cuts down on the number of memory allocations. Therefore
7435 * we will just issue a WARN here
7437 WARN("for non-ownerdata performance option not implemented.\n");
7440 return TRUE;
7443 /***
7444 * DESCRIPTION:
7445 * Sets the position of an item.
7447 * PARAMETER(S):
7448 * [I] infoPtr : valid pointer to the listview structure
7449 * [I] INT : item index
7450 * [I] LONG : x coordinate
7451 * [I] LONG : y coordinate
7453 * RETURN:
7454 * SUCCESS : TRUE
7455 * FAILURE : FALSE
7457 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem,
7458 LONG nPosX, LONG nPosY)
7460 UINT lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7461 UINT uView = lStyle & LVS_TYPEMASK;
7462 LISTVIEW_ITEM *lpItem;
7463 HDPA hdpaSubItems;
7464 BOOL bResult = FALSE;
7466 TRACE("(nItem=%d, X=%ld, Y=%ld)\n", nItem, nPosX, nPosY);
7468 if (lStyle & LVS_OWNERDATA)
7469 return FALSE;
7471 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7473 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7475 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7477 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7479 POINT orig;
7480 bResult = TRUE;
7481 orig = lpItem->ptPosition;
7482 if ((nPosX == -1) && (nPosY == -1))
7484 /* This point value seems to be an undocumented feature. The
7485 * best guess is that it means either at the origin, or at
7486 * the true beginning of the list. I will assume the origin.
7488 POINT pt1;
7489 if (!LISTVIEW_GetOrigin(infoPtr, &pt1))
7491 pt1.x = 0;
7492 pt1.y = 0;
7494 nPosX = pt1.x;
7495 nPosY = pt1.y;
7496 if (uView == LVS_ICON)
7498 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7499 nPosY += ICON_TOP_PADDING;
7501 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7502 nPosX, nPosY);
7505 lpItem->ptPosition.x = nPosX;
7506 lpItem->ptPosition.y = nPosY;
7507 if (uView == LVS_ICON)
7509 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7510 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7511 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7513 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7514 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7517 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7518 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7521 else
7523 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7524 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7532 return bResult;
7535 /***
7536 * DESCRIPTION:
7537 * Sets the state of one or many items.
7539 * PARAMETER(S):
7540 * [I] infoPtr : valid pointer to the listview structure
7541 * [I]INT : item index
7542 * [I] LPLVITEM : item or subitem info
7544 * RETURN:
7545 * SUCCESS : TRUE
7546 * FAILURE : FALSE
7548 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
7550 BOOL bResult = TRUE;
7551 LVITEMW lvItem;
7553 TRACE("(nItem=%d, lpLVItem=%s)\n",
7554 nItem, debuglvitem_t(lpLVItem, TRUE));
7556 ZeroMemory(&lvItem, sizeof(lvItem));
7557 lvItem.mask = LVIF_STATE;
7558 lvItem.state = lpLVItem->state;
7559 lvItem.stateMask = lpLVItem->stateMask ;
7560 lvItem.iItem = nItem;
7562 if (nItem == -1)
7564 /* apply to all items */
7565 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7566 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7568 else
7569 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7571 return bResult;
7574 /***
7575 * DESCRIPTION:
7576 * Sets the text of an item or subitem.
7578 * PARAMETER(S):
7579 * [I] hwnd : window handle
7580 * [I] nItem : item index
7581 * [I] lpLVItem : item or subitem info
7582 * [I] isW : TRUE if input is Unicode
7584 * RETURN:
7585 * SUCCESS : TRUE
7586 * FAILURE : FALSE
7588 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7590 BOOL bResult = FALSE;
7591 LVITEMW lvItem;
7593 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n",
7594 nItem, debuglvitem_t(lpLVItem, isW), isW);
7596 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7598 ZeroMemory(&lvItem, sizeof(LVITEMW));
7599 lvItem.mask = LVIF_TEXT;
7600 lvItem.pszText = lpLVItem->pszText;
7601 lvItem.iItem = nItem;
7602 lvItem.iSubItem = lpLVItem->iSubItem;
7603 if(isW) bResult = ListView_SetItemW(infoPtr->hwndSelf, &lvItem);
7604 else bResult = ListView_SetItemA(infoPtr->hwndSelf, &lvItem);
7607 return bResult;
7610 /***
7611 * DESCRIPTION:
7612 * Set item index that marks the start of a multiple selection.
7614 * PARAMETER(S):
7615 * [I] infoPtr : valid pointer to the listview structure
7616 * [I] INT : index
7618 * RETURN:
7619 * Index number or -1 if there is no selection mark.
7621 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7623 INT nOldIndex = infoPtr->nSelectionMark;
7625 TRACE("(nIndex=%d)\n", nIndex);
7627 infoPtr->nSelectionMark = nIndex;
7629 return nOldIndex;
7632 /***
7633 * DESCRIPTION:
7634 * Sets the text background color.
7636 * PARAMETER(S):
7637 * [I] infoPtr : valid pointer to the listview structure
7638 * [I] COLORREF : text background color
7640 * RETURN:
7641 * SUCCESS : TRUE
7642 * FAILURE : FALSE
7644 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7646 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7648 if (infoPtr->clrTextBk != clrTextBk)
7650 infoPtr->clrTextBk = clrTextBk;
7651 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7654 return TRUE;
7657 /***
7658 * DESCRIPTION:
7659 * Sets the text foreground color.
7661 * PARAMETER(S):
7662 * [I] infoPtr : valid pointer to the listview structure
7663 * [I] COLORREF : text color
7665 * RETURN:
7666 * SUCCESS : TRUE
7667 * FAILURE : FALSE
7669 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7671 TRACE("(clrText=%lx)\n", clrText);
7673 if (infoPtr->clrText != clrText)
7675 infoPtr->clrText = clrText;
7676 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7679 return TRUE;
7682 /* LISTVIEW_SetToolTips */
7683 /* LISTVIEW_SetUnicodeFormat */
7684 /* LISTVIEW_SetWorkAreas */
7686 /***
7687 * DESCRIPTION:
7688 * Callback internally used by LISTVIEW_SortItems()
7690 * PARAMETER(S):
7691 * [I] LPVOID : first LISTVIEW_ITEM to compare
7692 * [I] LPVOID : second LISTVIEW_ITEM to compare
7693 * [I] LPARAM : HWND of control
7695 * RETURN:
7696 * if first comes before second : negative
7697 * if first comes after second : positive
7698 * if first and second are equivalent : zero
7700 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7702 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7703 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7704 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7706 /* Forward the call to the client defined callback */
7707 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7710 /***
7711 * DESCRIPTION:
7712 * Sorts the listview items.
7714 * PARAMETER(S):
7715 * [I] infoPtr : valid pointer to the listview structure
7716 * [I] WPARAM : application-defined value
7717 * [I] LPARAM : pointer to comparision callback
7719 * RETURN:
7720 * SUCCESS : TRUE
7721 * FAILURE : FALSE
7723 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7725 UINT lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7726 HDPA hdpaSubItems=NULL;
7727 LISTVIEW_ITEM *pLVItem=NULL;
7728 LPVOID selectionMarkItem;
7729 int nCount, i;
7731 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7733 if (lStyle & LVS_OWNERDATA) return FALSE;
7735 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7737 nCount = GETITEMCOUNT(infoPtr);
7738 /* if there are 0 or 1 items, there is no need to sort */
7739 if (nCount < 2)
7740 return TRUE;
7742 infoPtr->pfnCompare = pfnCompare;
7743 infoPtr->lParamSort = lParamSort;
7744 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
7746 /* Adjust selections and indices so that they are the way they should
7747 * be after the sort (otherwise, the list items move around, but
7748 * whatever is at the item's previous original position will be
7749 * selected instead)
7751 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7752 for (i=0; i < nCount; i++)
7754 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7755 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7757 if (pLVItem->state & LVIS_SELECTED)
7758 LISTVIEW_AddSelectionRange(infoPtr, i, i);
7759 else
7760 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
7761 if (pLVItem->state & LVIS_FOCUSED)
7762 infoPtr->nFocusedItem=i;
7764 if (selectionMarkItem != NULL)
7765 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7766 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7768 /* align the items */
7769 LISTVIEW_AlignTop(infoPtr);
7771 /* refresh the display */
7772 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7774 return TRUE;
7777 /* LISTVIEW_SubItemHitTest */
7779 /***
7780 * DESCRIPTION:
7781 * Updates an items or rearranges the listview control.
7783 * PARAMETER(S):
7784 * [I] infoPtr : valid pointer to the listview structure
7785 * [I] INT : item index
7787 * RETURN:
7788 * SUCCESS : TRUE
7789 * FAILURE : FALSE
7791 static LRESULT LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7793 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7794 BOOL bResult = FALSE;
7795 RECT rc;
7797 TRACE("(nItem=%d)\n", nItem);
7799 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7801 bResult = TRUE;
7803 /* rearrange with default alignment style */
7804 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7805 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7807 ListView_Arrange(infoPtr->hwndSelf, 0);
7809 else
7811 /* get item bounding rectangle */
7812 ListView_GetItemRect(infoPtr->hwndSelf, nItem, &rc, LVIR_BOUNDS);
7813 InvalidateRect(infoPtr->hwndSelf, &rc, TRUE);
7817 return bResult;
7820 /***
7821 * DESCRIPTION:
7822 * Creates the listview control.
7824 * PARAMETER(S):
7825 * [I] hwnd : window handle
7826 * [I] lpcs : the create parameters
7828 * RETURN:
7829 * Success: 0
7830 * Failure: -1
7832 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7834 LISTVIEW_INFO *infoPtr;
7835 UINT uView = lpcs->style & LVS_TYPEMASK;
7836 LOGFONTW logFont;
7838 TRACE("(lpcs=%p)\n", lpcs);
7840 /* initialize info pointer */
7841 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7842 if (!infoPtr) return -1;
7844 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7845 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7847 infoPtr->hwndSelf = hwnd;
7848 /* determine the type of structures to use */
7849 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7850 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7852 /* initialize color information */
7853 infoPtr->clrBk = comctl32_color.clrWindow;
7854 infoPtr->clrText = comctl32_color.clrWindowText;
7855 infoPtr->clrTextBk = CLR_DEFAULT;
7857 /* set default values */
7858 infoPtr->uCallbackMask = 0;
7859 infoPtr->nFocusedItem = -1;
7860 infoPtr->nSelectionMark = -1;
7861 infoPtr->nHotItem = -1;
7862 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7863 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7864 infoPtr->hwndEdit = 0;
7865 infoPtr->bEditing = FALSE;
7866 infoPtr->nEditLabelItem = -1;
7867 infoPtr->bIsDrawing = FALSE;
7869 /* get default font (icon title) */
7870 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7871 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7872 infoPtr->hFont = infoPtr->hDefaultFont;
7873 LISTVIEW_SaveTextMetrics(infoPtr);
7875 /* create header */
7876 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7877 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7878 0, 0, 0, 0, hwnd, (HMENU)0,
7879 lpcs->hInstance, NULL);
7881 /* set header unicode format */
7882 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
7884 /* set header font */
7885 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7886 (LPARAM)TRUE);
7888 if (uView == LVS_ICON)
7890 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7891 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7893 else if (uView == LVS_REPORT)
7895 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7897 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7899 else
7901 /* set HDS_HIDDEN flag to hide the header bar */
7902 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7903 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7907 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7908 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7910 else
7912 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7913 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7916 /* display unsupported listview window styles */
7917 LISTVIEW_UnsupportedStyles(lpcs->style);
7919 /* allocate memory for the data structure */
7920 infoPtr->hdpaItems = DPA_Create(10);
7922 /* allocate memory for the selection ranges */
7923 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7925 /* initialize size of items */
7926 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
7927 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7929 /* initialize the hover time to -1(indicating the default system hover time) */
7930 infoPtr->dwHoverTime = -1;
7932 return 0;
7935 /***
7936 * DESCRIPTION:
7937 * Erases the background of the listview control.
7939 * PARAMETER(S):
7940 * [I] infoPtr : valid pointer to the listview structure
7941 * [I] WPARAM : device context handle
7942 * [I] LPARAM : not used
7944 * RETURN:
7945 * SUCCESS : TRUE
7946 * FAILURE : FALSE
7948 static LRESULT LISTVIEW_EraseBackground(LISTVIEW_INFO *infoPtr, WPARAM wParam,
7949 LPARAM lParam)
7951 BOOL bResult;
7953 TRACE("(wParam=%x, lParam=%lx)\n", wParam, lParam);
7955 if (infoPtr->clrBk == CLR_NONE)
7957 bResult = SendMessageW(GetParent(infoPtr->hwndSelf), WM_ERASEBKGND, wParam, lParam);
7959 else
7961 RECT rc;
7962 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7963 GetClientRect(infoPtr->hwndSelf, &rc);
7964 FillRect((HDC)wParam, &rc, hBrush);
7965 DeleteObject(hBrush);
7966 bResult = TRUE;
7969 return bResult;
7973 static void LISTVIEW_FillBackground(LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rc)
7975 TRACE("(hdc=%x, rc=%p)\n", hdc, rc);
7977 if (infoPtr->clrBk != CLR_NONE)
7979 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7980 FillRect(hdc, rc, hBrush);
7981 DeleteObject(hBrush);
7985 /***
7986 * DESCRIPTION:
7987 * Performs vertical scrolling.
7989 * PARAMETER(S):
7990 * [I] infoPtr : valid pointer to the listview structure
7991 * [I] nScrollCode : scroll code
7992 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7993 * [I] hScrollWnd : scrollbar control window handle
7995 * RETURN:
7996 * Zero
7998 * NOTES:
7999 * SB_LINEUP/SB_LINEDOWN:
8000 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8001 * for LVS_REPORT is 1 line
8002 * for LVS_LIST cannot occur
8005 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8006 INT nScrollDiff, HWND hScrollWnd)
8008 UINT uView = get_listview_type(infoPtr);
8009 INT nOldScrollPos, nNewScrollPos;
8010 SCROLLINFO scrollInfo;
8011 BOOL is_an_icon;
8013 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
8015 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8017 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8018 scrollInfo.cbSize = sizeof(SCROLLINFO);
8019 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8021 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8023 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8025 nOldScrollPos = scrollInfo.nPos;
8026 switch (nScrollCode)
8028 case SB_INTERNAL:
8029 break;
8031 case SB_LINEUP:
8032 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8033 break;
8035 case SB_LINEDOWN:
8036 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8037 break;
8039 case SB_PAGEUP:
8040 nScrollDiff = -scrollInfo.nPage;
8041 break;
8043 case SB_PAGEDOWN:
8044 nScrollDiff = scrollInfo.nPage;
8045 break;
8047 case SB_THUMBPOSITION:
8048 case SB_THUMBTRACK:
8049 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8050 break;
8052 default:
8053 nScrollDiff = 0;
8056 /* quit right away if pos isn't changing */
8057 if (nScrollDiff == 0) return 0;
8059 /* calculate new position, and handle overflows */
8060 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8061 if (nScrollDiff > 0) {
8062 if (nNewScrollPos < nOldScrollPos ||
8063 nNewScrollPos > scrollInfo.nMax)
8064 nNewScrollPos = scrollInfo.nMax;
8065 } else {
8066 if (nNewScrollPos > nOldScrollPos ||
8067 nNewScrollPos < scrollInfo.nMin)
8068 nNewScrollPos = scrollInfo.nMin;
8071 /* set the new position, and reread in case it changed */
8072 scrollInfo.fMask = SIF_POS;
8073 scrollInfo.nPos = nNewScrollPos;
8074 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8076 /* carry on only if it really changed */
8077 if (nNewScrollPos == nOldScrollPos) return 0;
8079 /* now adjust to client coordinates */
8080 nScrollDiff = nOldScrollPos - nNewScrollPos;
8081 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8083 /* and scroll the window */
8084 ScrollWindowEx( infoPtr->hwndSelf, 0, nScrollDiff, &infoPtr->rcList,
8085 &infoPtr->rcList, 0, 0, SW_INVALIDATE);
8087 return 0;
8090 /***
8091 * DESCRIPTION:
8092 * Performs horizontal scrolling.
8094 * PARAMETER(S):
8095 * [I] infoPtr : valid pointer to the listview structure
8096 * [I] nScrollCode : scroll code
8097 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8098 * [I] hScrollWnd : scrollbar control window handle
8100 * RETURN:
8101 * Zero
8103 * NOTES:
8104 * SB_LINELEFT/SB_LINERIGHT:
8105 * for LVS_ICON, LVS_SMALLICON 1 pixel
8106 * for LVS_REPORT is 1 pixel
8107 * for LVS_LIST is 1 column --> which is a 1 because the
8108 * scroll is based on columns not pixels
8111 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8112 INT nScrollDiff, HWND hScrollWnd)
8114 UINT uView = get_listview_type(infoPtr);
8115 INT nOldScrollPos, nNewScrollPos;
8116 SCROLLINFO scrollInfo;
8118 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
8120 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8122 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8123 scrollInfo.cbSize = sizeof(SCROLLINFO);
8124 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8126 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8128 nOldScrollPos = scrollInfo.nPos;
8130 switch (nScrollCode)
8132 case SB_INTERNAL:
8133 break;
8135 case SB_LINELEFT:
8136 nScrollDiff = -1;
8137 break;
8139 case SB_LINERIGHT:
8140 nScrollDiff = 1;
8141 break;
8143 case SB_PAGELEFT:
8144 nScrollDiff = -scrollInfo.nPage;
8145 break;
8147 case SB_PAGERIGHT:
8148 nScrollDiff = scrollInfo.nPage;
8149 break;
8151 case SB_THUMBPOSITION:
8152 case SB_THUMBTRACK:
8153 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8154 break;
8156 default:
8157 nScrollDiff = 0;
8160 /* quit right away if pos isn't changing */
8161 if (nScrollDiff == 0) return 0;
8163 /* calculate new position, and handle overflows */
8164 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8165 if (nScrollDiff > 0) {
8166 if (nNewScrollPos < nOldScrollPos ||
8167 nNewScrollPos > scrollInfo.nMax)
8168 nNewScrollPos = scrollInfo.nMax;
8169 } else {
8170 if (nNewScrollPos > nOldScrollPos ||
8171 nNewScrollPos < scrollInfo.nMin)
8172 nNewScrollPos = scrollInfo.nMin;
8175 /* set the new position, and reread in case it changed */
8176 scrollInfo.fMask = SIF_POS;
8177 scrollInfo.nPos = nNewScrollPos;
8178 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8180 /* carry on only if it really changed */
8181 if (nNewScrollPos == nOldScrollPos) return 0;
8183 if(uView == LVS_REPORT)
8184 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8186 /* now adjust to client coordinates */
8187 nScrollDiff = nOldScrollPos - nNewScrollPos;
8188 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8190 /* and scroll the window */
8191 ScrollWindowEx( infoPtr->hwndSelf, nScrollDiff, 0, &infoPtr->rcList,
8192 &infoPtr->rcList, 0, 0, SW_INVALIDATE);
8194 return 0;
8197 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8199 UINT uView = get_listview_type(infoPtr);
8200 INT gcWheelDelta = 0;
8201 UINT pulScrollLines = 3;
8202 SCROLLINFO scrollInfo;
8204 TRACE("(wheelDelta=%d)\n", wheelDelta);
8206 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8207 gcWheelDelta -= wheelDelta;
8209 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8210 scrollInfo.cbSize = sizeof(SCROLLINFO);
8211 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8213 switch(uView)
8215 case LVS_ICON:
8216 case LVS_SMALLICON:
8218 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8219 * should be fixed in the future.
8221 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
8222 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
8223 scrollInfo.nPos + (gcWheelDelta < 0) ?
8224 LISTVIEW_SCROLL_ICON_LINE_SIZE :
8225 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8226 break;
8228 case LVS_REPORT:
8229 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8231 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
8233 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8234 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8235 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8238 break;
8240 case LVS_LIST:
8241 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8242 break;
8244 return 0;
8247 /***
8248 * DESCRIPTION:
8249 * ???
8251 * PARAMETER(S):
8252 * [I] infoPtr : valid pointer to the listview structure
8253 * [I] INT : virtual key
8254 * [I] LONG : key data
8256 * RETURN:
8257 * Zero
8259 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8261 UINT uView = get_listview_type(infoPtr);
8262 INT nItem = -1;
8263 NMLVKEYDOWN nmKeyDown;
8265 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
8267 /* send LVN_KEYDOWN notification */
8268 nmKeyDown.wVKey = nVirtualKey;
8269 nmKeyDown.flags = 0;
8270 notify(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8272 switch (nVirtualKey)
8274 case VK_RETURN:
8275 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8277 notify_return(infoPtr);
8278 notify_itemactivate(infoPtr);
8280 break;
8282 case VK_HOME:
8283 if (GETITEMCOUNT(infoPtr) > 0)
8284 nItem = 0;
8285 break;
8287 case VK_END:
8288 if (GETITEMCOUNT(infoPtr) > 0)
8289 nItem = GETITEMCOUNT(infoPtr) - 1;
8290 break;
8292 case VK_LEFT:
8293 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8294 break;
8296 case VK_UP:
8297 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8298 break;
8300 case VK_RIGHT:
8301 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8302 break;
8304 case VK_DOWN:
8305 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8306 break;
8308 case VK_PRIOR:
8309 if (uView == LVS_REPORT)
8310 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
8311 else
8312 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8313 * LISTVIEW_GetCountPerRow(infoPtr);
8314 if(nItem < 0) nItem = 0;
8315 break;
8317 case VK_NEXT:
8318 if (uView == LVS_REPORT)
8319 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
8320 else
8321 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8322 * LISTVIEW_GetCountPerRow(infoPtr);
8323 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8324 break;
8327 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8329 if (LISTVIEW_KeySelection(infoPtr, nItem))
8330 UpdateWindow(infoPtr->hwndSelf); /* update client area */
8333 return 0;
8336 /***
8337 * DESCRIPTION:
8338 * Kills the focus.
8340 * PARAMETER(S):
8341 * [I] infoPtr : valid pointer to the listview structure
8343 * RETURN:
8344 * Zero
8346 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8348 UINT uView = get_listview_type(infoPtr);
8349 INT i,nTop,nBottom;
8351 TRACE("()\n");
8353 /* send NM_KILLFOCUS notification */
8354 notify_killfocus(infoPtr);
8356 /* set window focus flag */
8357 infoPtr->bFocus = FALSE;
8359 /* NEED drawing optimization ; redraw the selected items */
8360 if (uView & LVS_REPORT)
8362 nTop = LISTVIEW_GetTopIndex(infoPtr);
8363 nBottom = nTop +
8364 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8366 else
8368 nTop = 0;
8369 nBottom = GETITEMCOUNT(infoPtr);
8371 for (i = nTop; i<nBottom; i++)
8373 if (LISTVIEW_IsSelected(infoPtr,i))
8375 RECT rcItem;
8376 rcItem.left = LVIR_BOUNDS;
8377 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
8378 InvalidateRect(infoPtr->hwndSelf, &rcItem, FALSE);
8382 return 0;
8385 /***
8386 * DESCRIPTION:
8387 * Processes double click messages (left mouse button).
8389 * PARAMETER(S):
8390 * [I] infoPtr : valid pointer to the listview structure
8391 * [I] wKey : key flag
8392 * [I] pts : mouse coordinate
8394 * RETURN:
8395 * Zero
8397 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8399 LVHITTESTINFO htInfo;
8400 NMLISTVIEW nmlv;
8402 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8404 htInfo.pt.x = pts.x;
8405 htInfo.pt.y = pts.y;
8407 /* send NM_DBLCLK notification */
8408 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8409 if (LISTVIEW_HitTestItem(infoPtr, &htInfo, TRUE) != -1)
8411 nmlv.iItem = htInfo.iItem;
8412 nmlv.iSubItem = htInfo.iSubItem;
8414 else
8416 nmlv.iItem = -1;
8417 nmlv.iSubItem = 0;
8419 nmlv.ptAction.x = pts.x;
8420 nmlv.ptAction.y = pts.y;
8421 listview_notify(infoPtr, NM_DBLCLK, &nmlv);
8424 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8425 if(nmlv.iItem != -1)
8426 notify_itemactivate(infoPtr);
8428 return 0;
8431 /***
8432 * DESCRIPTION:
8433 * Processes mouse down messages (left mouse button).
8435 * PARAMETER(S):
8436 * [I] infoPtr : valid pointer to the listview structure
8437 * [I] wKey : key flag
8438 * [I] pts : mouse coordinate
8440 * RETURN:
8441 * Zero
8443 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8445 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
8446 static BOOL bGroupSelect = TRUE;
8447 POINT pt = { pts.x, pts.y };
8448 INT nItem;
8450 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8452 /* send NM_RELEASEDCAPTURE notification */
8453 notify_releasedcapture(infoPtr);
8455 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8457 /* set left button down flag */
8458 infoPtr->bLButtonDown = TRUE;
8460 nItem = LISTVIEW_MouseSelection(infoPtr, pt);
8461 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8463 if (lStyle & LVS_SINGLESEL)
8465 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8466 && infoPtr->nEditLabelItem == -1)
8467 infoPtr->nEditLabelItem = nItem;
8468 else
8469 LISTVIEW_SetSelection(infoPtr, nItem);
8471 else
8473 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8475 if (bGroupSelect)
8476 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8477 else
8478 LISTVIEW_AddSelection(infoPtr, nItem);
8480 else if (wKey & MK_CONTROL)
8482 bGroupSelect = LISTVIEW_ToggleSelection(infoPtr, nItem);
8484 else if (wKey & MK_SHIFT)
8486 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8488 else
8490 BOOL was_selected =
8491 (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8493 /* set selection (clears other pre-existing selections) */
8494 LISTVIEW_SetSelection(infoPtr, nItem);
8496 if (was_selected && infoPtr->nEditLabelItem == -1)
8497 infoPtr->nEditLabelItem = nItem;
8501 else
8503 /* remove all selections */
8504 LISTVIEW_RemoveAllSelections(infoPtr);
8507 /* redraw if we could have possibly selected something */
8508 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8510 return 0;
8513 /***
8514 * DESCRIPTION:
8515 * Processes mouse up messages (left mouse button).
8517 * PARAMETER(S):
8518 * [I] infoPtr : valid pointer to the listview structure
8519 * [I] wKey : key flag
8520 * [I] pts : mouse coordinate
8522 * RETURN:
8523 * Zero
8525 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8527 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8529 if (infoPtr->bLButtonDown)
8531 LVHITTESTINFO lvHitTestInfo;
8532 NMLISTVIEW nmlv;
8534 lvHitTestInfo.pt.x = pts.x;
8535 lvHitTestInfo.pt.y = pts.y;
8537 /* send NM_CLICK notification */
8538 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8539 if (LISTVIEW_HitTestItem(infoPtr, &lvHitTestInfo, TRUE) != -1)
8541 nmlv.iItem = lvHitTestInfo.iItem;
8542 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8544 else
8546 nmlv.iItem = -1;
8547 nmlv.iSubItem = 0;
8549 nmlv.ptAction.x = pts.x;
8550 nmlv.ptAction.y = pts.y;
8551 listview_notify(infoPtr, NM_CLICK, &nmlv);
8553 /* set left button flag */
8554 infoPtr->bLButtonDown = FALSE;
8556 if(infoPtr->nEditLabelItem != -1)
8558 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL) {
8559 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8561 infoPtr->nEditLabelItem = -1;
8565 return 0;
8568 /***
8569 * DESCRIPTION:
8570 * Destroys the listview control (called after WM_DESTROY).
8572 * PARAMETER(S):
8573 * [I] infoPtr : valid pointer to the listview structure
8575 * RETURN:
8576 * Zero
8578 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8580 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
8582 TRACE("()\n");
8584 /* delete all items */
8585 LISTVIEW_DeleteAllItems(infoPtr);
8587 /* destroy data structure */
8588 DPA_Destroy(infoPtr->hdpaItems);
8589 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8591 /* destroy image lists */
8592 if (!(lStyle & LVS_SHAREIMAGELISTS))
8594 #if 0
8595 /* FIXME: If the caller does a ImageList_Destroy and then we
8596 * do this code the area will be freed twice. Currently
8597 * this generates an "err:heap:HEAP_ValidateInUseArena
8598 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
8599 * has PREV_FREE flag" sometimes.
8601 * We will leak the memory till we figure out how to fix
8603 if (infoPtr->himlNormal)
8604 ImageList_Destroy(infoPtr->himlNormal);
8605 if (infoPtr->himlSmall)
8606 ImageList_Destroy(infoPtr->himlSmall);
8607 if (infoPtr->himlState)
8608 ImageList_Destroy(infoPtr->himlState);
8609 #endif
8612 /* destroy font */
8613 infoPtr->hFont = (HFONT)0;
8614 if (infoPtr->hDefaultFont)
8616 DeleteObject(infoPtr->hDefaultFont);
8619 /* free listview info pointer*/
8620 COMCTL32_Free(infoPtr);
8622 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
8623 return 0;
8626 /***
8627 * DESCRIPTION:
8628 * Handles notifications from children.
8630 * PARAMETER(S):
8631 * [I] infoPtr : valid pointer to the listview structure
8632 * [I] INT : control identifier
8633 * [I] LPNMHDR : notification information
8635 * RETURN:
8636 * Zero
8638 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
8640 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
8642 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8644 /* handle notification from header control */
8645 if (lpnmh->code == HDN_ENDTRACKW)
8647 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8648 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8650 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
8652 /* Handle sorting by Header Column */
8653 NMLISTVIEW nmlv;
8655 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8656 nmlv.iItem = -1;
8657 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8658 listview_notify(infoPtr, LVN_COLUMNCLICK, &nmlv);
8660 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8662 /* Idealy this should be done in HDN_ENDTRACKA
8663 * but since SetItemBounds in Header.c is called after
8664 * the notification is sent, it is neccessary to handle the
8665 * update of the scroll bar here (Header.c works fine as it is,
8666 * no need to disturb it)
8668 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8669 LISTVIEW_UpdateScroll(infoPtr);
8670 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8675 return 0;
8678 /***
8679 * DESCRIPTION:
8680 * Determines the type of structure to use.
8682 * PARAMETER(S):
8683 * [I] infoPtr : valid pointer to the listview structureof the sender
8684 * [I] HWND : listview window handle
8685 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8687 * RETURN:
8688 * Zero
8690 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8692 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
8694 if (nCommand == NF_REQUERY)
8695 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8696 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8697 return 0;
8700 /***
8701 * DESCRIPTION:
8702 * Paints/Repaints the listview control.
8704 * PARAMETER(S):
8705 * [I] infoPtr : valid pointer to the listview structure
8706 * [I] HDC : device context handle
8708 * RETURN:
8709 * Zero
8711 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8713 PAINTSTRUCT ps;
8715 TRACE("(hdc=%x)\n", hdc);
8717 if (hdc == 0)
8719 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8720 LISTVIEW_Refresh(infoPtr, hdc);
8721 EndPaint(infoPtr->hwndSelf, &ps);
8723 else
8725 LISTVIEW_Refresh(infoPtr, hdc);
8728 return 0;
8731 /***
8732 * DESCRIPTION:
8733 * Processes double click messages (right mouse button).
8735 * PARAMETER(S):
8736 * [I] infoPtr : valid pointer to the listview structure
8737 * [I] wKey : key flag
8738 * [I] pts : mouse coordinate
8740 * RETURN:
8741 * Zero
8743 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8745 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8747 /* send NM_RELEASEDCAPTURE notification */
8748 notify_releasedcapture(infoPtr);
8750 /* send NM_RDBLCLK notification */
8751 notify_rdblclk(infoPtr);
8753 return 0;
8756 /***
8757 * DESCRIPTION:
8758 * Processes mouse down messages (right mouse button).
8760 * PARAMETER(S):
8761 * [I] infoPtr : valid pointer to the listview structure
8762 * [I] wKey : key flag
8763 * [I] pts : mouse coordinate
8765 * RETURN:
8766 * Zero
8768 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8770 INT nItem;
8771 NMLISTVIEW nmlv;
8772 LVHITTESTINFO lvHitTestInfo;
8773 POINT pt = { pts.x, pts.y };
8775 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8777 /* send NM_RELEASEDCAPTURE notification */
8778 notify_releasedcapture(infoPtr);
8780 /* make sure the listview control window has the focus */
8781 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8783 /* set right button down flag */
8784 infoPtr->bRButtonDown = TRUE;
8786 /* determine the index of the selected item */
8787 nItem = LISTVIEW_MouseSelection(infoPtr, pt);
8788 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8790 LISTVIEW_SetItemFocus(infoPtr,nItem);
8791 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8792 !LISTVIEW_IsSelected(infoPtr,nItem))
8793 LISTVIEW_SetSelection(infoPtr, nItem);
8795 else
8797 LISTVIEW_RemoveAllSelections(infoPtr);
8800 lvHitTestInfo.pt.x = pts.x;
8801 lvHitTestInfo.pt.y = pts.y;
8803 /* Send NM_RClICK notification */
8804 ZeroMemory(&nmlv, sizeof(nmlv));
8805 if (LISTVIEW_HitTestItem(infoPtr, &lvHitTestInfo, TRUE) != -1)
8807 nmlv.iItem = lvHitTestInfo.iItem;
8808 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8810 else
8812 nmlv.iItem = -1;
8813 nmlv.iSubItem = 0;
8815 nmlv.ptAction.x = pts.x;
8816 nmlv.ptAction.y = pts.y;
8817 listview_notify(infoPtr, NM_RCLICK, &nmlv);
8819 return 0;
8822 /***
8823 * DESCRIPTION:
8824 * Processes mouse up messages (right mouse button).
8826 * PARAMETER(S):
8827 * [I] infoPtr : valid pointer to the listview structure
8828 * [I] wKey : key flag
8829 * [I] pts : mouse coordinate
8831 * RETURN:
8832 * Zero
8834 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8836 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8838 if (infoPtr->bRButtonDown)
8840 POINT pt = { pts.x, pts.y };
8842 /* set button flag */
8843 infoPtr->bRButtonDown = FALSE;
8845 /* Change to screen coordinate for WM_CONTEXTMENU */
8846 ClientToScreen(infoPtr->hwndSelf, &pt);
8848 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8849 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8850 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8853 return 0;
8857 /***
8858 * DESCRIPTION:
8859 * Sets the cursor.
8861 * PARAMETER(S):
8862 * [I] infoPtr : valid pointer to the listview structure
8863 * [I] hwnd : window handle of window containing the cursor
8864 * [I] nHittest : hit-test code
8865 * [I] wMouseMsg : ideintifier of the mouse message
8867 * RETURN:
8868 * TRUE if cursor is set
8869 * FALSE otherwise
8871 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8873 POINT pt;
8875 if(!(infoPtr->dwExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8877 GetCursorPos(&pt);
8878 if (LISTVIEW_MouseSelection(infoPtr, pt) < 0) return FALSE;
8880 SetCursor(infoPtr->hHotCursor);
8882 return TRUE;
8885 /***
8886 * DESCRIPTION:
8887 * Sets the focus.
8889 * PARAMETER(S):
8890 * [I] infoPtr : valid pointer to the listview structure
8891 * [I] infoPtr : valid pointer to the listview structureof previously focused window
8893 * RETURN:
8894 * Zero
8896 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8898 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
8900 /* send NM_SETFOCUS notification */
8901 notify_setfocus(infoPtr);
8903 /* set window focus flag */
8904 infoPtr->bFocus = TRUE;
8906 UpdateWindow(infoPtr->hwndSelf);
8908 return 0;
8911 /***
8912 * DESCRIPTION:
8913 * Sets the font.
8915 * PARAMETER(S):
8916 * [I] infoPtr : valid pointer to the listview structure
8917 * [I] HFONT : font handle
8918 * [I] WORD : redraw flag
8920 * RETURN:
8921 * Zero
8923 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8925 UINT uView = get_listview_type(infoPtr);
8927 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
8929 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8930 LISTVIEW_SaveTextMetrics(infoPtr);
8932 if (uView == LVS_REPORT)
8934 /* set header font */
8935 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
8936 MAKELPARAM(fRedraw, 0));
8939 /* invalidate listview control client area */
8940 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8942 if (fRedraw) UpdateWindow(infoPtr->hwndSelf);
8944 return 0;
8947 /***
8948 * DESCRIPTION:
8949 * Message handling for WM_SETREDRAW.
8950 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8952 * PARAMETER(S):
8953 * [I] infoPtr : valid pointer to the listview structure
8954 * [I] bRedraw: state of redraw flag
8956 * RETURN:
8957 * DefWinProc return value
8959 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8961 LRESULT lResult = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, bRedraw, 0);
8962 if(bRedraw)
8963 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8964 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8965 return lResult;
8968 /***
8969 * DESCRIPTION:
8970 * Resizes the listview control. This function processes WM_SIZE
8971 * messages. At this time, the width and height are not used.
8973 * PARAMETER(S):
8974 * [I] infoPtr : valid pointer to the listview structure
8975 * [I] WORD : new width
8976 * [I] WORD : new height
8978 * RETURN:
8979 * Zero
8981 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8983 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
8984 UINT uView = lStyle & LVS_TYPEMASK;
8986 TRACE("(width=%d, height=%d)\n", Width, Height);
8988 if (LISTVIEW_UpdateSize(infoPtr))
8990 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8992 if (lStyle & LVS_ALIGNLEFT)
8993 LISTVIEW_AlignLeft(infoPtr);
8994 else
8995 LISTVIEW_AlignTop(infoPtr);
8998 LISTVIEW_UpdateScroll(infoPtr);
9000 /* invalidate client area + erase background */
9001 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9004 return 0;
9007 /***
9008 * DESCRIPTION:
9009 * Sets the size information.
9011 * PARAMETER(S):
9012 * [I] infoPtr : valid pointer to the listview structure
9014 * RETURN:
9015 * Zero if no size change
9016 * 1 of size changed
9018 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9020 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
9021 UINT uView = lStyle & LVS_TYPEMASK;
9022 RECT rcList;
9023 RECT rcOld;
9025 GetClientRect(infoPtr->hwndSelf, &rcList);
9026 CopyRect(&rcOld,&(infoPtr->rcList));
9027 infoPtr->rcList.left = 0;
9028 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9029 infoPtr->rcList.top = 0;
9030 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9032 if (uView == LVS_LIST)
9034 /* Apparently the "LIST" style is supposed to have the same
9035 * number of items in a column even if there is no scroll bar.
9036 * Since if a scroll bar already exists then the bottom is already
9037 * reduced, only reduce if the scroll bar does not currently exist.
9038 * The "2" is there to mimic the native control. I think it may be
9039 * related to either padding or edges. (GLA 7/2002)
9041 if (!(lStyle & WS_HSCROLL))
9043 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9044 if (infoPtr->rcList.bottom > nHScrollHeight)
9045 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
9047 else
9049 if (infoPtr->rcList.bottom > 2)
9050 infoPtr->rcList.bottom -= 2;
9053 else if (uView == LVS_REPORT)
9055 HDLAYOUT hl;
9056 WINDOWPOS wp;
9058 hl.prc = &rcList;
9059 hl.pwpos = &wp;
9060 Header_Layout(infoPtr->hwndHeader, &hl);
9062 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9064 if (!(LVS_NOCOLUMNHEADER & lStyle))
9065 infoPtr->rcList.top = max(wp.cy, 0);
9067 return (EqualRect(&rcOld,&(infoPtr->rcList)));
9070 /***
9071 * DESCRIPTION:
9072 * Processes WM_STYLECHANGED messages.
9074 * PARAMETER(S):
9075 * [I] infoPtr : valid pointer to the listview structure
9076 * [I] WPARAM : window style type (normal or extended)
9077 * [I] LPSTYLESTRUCT : window style information
9079 * RETURN:
9080 * Zero
9082 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9083 LPSTYLESTRUCT lpss)
9085 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9086 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9087 RECT rcList = infoPtr->rcList;
9089 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
9090 wStyleType, lpss->styleOld, lpss->styleNew);
9092 if (wStyleType == GWL_STYLE)
9094 if (uOldView == LVS_REPORT)
9095 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9097 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9098 ((lpss->styleNew & WS_HSCROLL) == 0))
9099 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9101 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9102 ((lpss->styleNew & WS_VSCROLL) == 0))
9103 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9105 /* If switching modes, then start with no scroll bars and then
9106 * decide.
9108 if (uNewView != uOldView)
9109 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9111 if (uNewView == LVS_ICON)
9113 INT oldcx, oldcy;
9115 /* First readjust the iconSize and if necessary the iconSpacing */
9116 oldcx = infoPtr->iconSize.cx;
9117 oldcy = infoPtr->iconSize.cy;
9118 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9119 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9120 if (infoPtr->himlNormal != NULL)
9122 INT cx, cy;
9123 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
9124 infoPtr->iconSize.cx = cx;
9125 infoPtr->iconSize.cy = cy;
9127 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
9129 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
9130 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9131 LISTVIEW_SetIconSpacing(infoPtr,0);
9134 /* Now update the full item width and height */
9135 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
9136 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
9137 if (lpss->styleNew & LVS_ALIGNLEFT)
9138 LISTVIEW_AlignLeft(infoPtr);
9139 else
9140 LISTVIEW_AlignTop(infoPtr);
9142 else if (uNewView == LVS_REPORT)
9144 HDLAYOUT hl;
9145 WINDOWPOS wp;
9147 hl.prc = &rcList;
9148 hl.pwpos = &wp;
9149 Header_Layout(infoPtr->hwndHeader, &hl);
9150 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9151 wp.flags);
9152 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9153 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9155 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9156 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9157 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
9158 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
9160 else if (uNewView == LVS_LIST)
9162 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9163 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9164 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
9165 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
9167 else
9169 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9170 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9171 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
9172 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
9173 if (lpss->styleNew & LVS_ALIGNLEFT)
9174 LISTVIEW_AlignLeft(infoPtr);
9175 else
9176 LISTVIEW_AlignTop(infoPtr);
9179 /* update the size of the client area */
9180 LISTVIEW_UpdateSize(infoPtr);
9182 /* add scrollbars if needed */
9183 LISTVIEW_UpdateScroll(infoPtr);
9185 /* invalidate client area + erase background */
9186 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9188 /* print the list of unsupported window styles */
9189 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9192 /* If they change the view and we have an active edit control
9193 we will need to kill the control since the redraw will
9194 misplace the edit control.
9196 if (infoPtr->bEditing &&
9197 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9198 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9200 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9203 return 0;
9206 /***
9207 * DESCRIPTION:
9208 * Window procedure of the listview control.
9211 static LRESULT WINAPI
9212 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9214 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9216 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9218 if (!infoPtr && (uMsg != WM_CREATE))
9219 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9221 switch (uMsg)
9223 case LVM_APPROXIMATEVIEWRECT:
9224 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9225 LOWORD(lParam), HIWORD(lParam));
9226 case LVM_ARRANGE:
9227 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9229 /* case LVN_CANCELEDITLABEL */
9231 /* case LVM_CREATEDRAGIMAGE: */
9233 case LVM_DELETEALLITEMS:
9234 return LISTVIEW_DeleteAllItems(infoPtr);
9236 case LVM_DELETECOLUMN:
9237 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9239 case LVM_DELETEITEM:
9240 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9242 case LVM_EDITLABELW:
9243 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9245 case LVM_EDITLABELA:
9246 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9248 /* case LVN_ENABLEGROUPVIEW: */
9250 case LVM_ENSUREVISIBLE:
9251 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9253 case LVM_FINDITEMW:
9254 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9256 case LVM_FINDITEMA:
9257 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9259 case LVM_GETBKCOLOR:
9260 return infoPtr->clrBk;
9262 /* case LVM_GETBKIMAGE: */
9264 case LVM_GETCALLBACKMASK:
9265 return infoPtr->uCallbackMask;
9267 case LVM_GETCOLUMNA:
9268 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9270 case LVM_GETCOLUMNW:
9271 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9273 case LVM_GETCOLUMNORDERARRAY:
9274 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9276 case LVM_GETCOLUMNWIDTH:
9277 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9279 case LVM_GETCOUNTPERPAGE:
9280 return LISTVIEW_GetCountPerPage(infoPtr);
9282 case LVM_GETEDITCONTROL:
9283 return (LRESULT)infoPtr->hwndEdit;
9285 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9286 return infoPtr->dwExStyle;
9288 case LVM_GETHEADER:
9289 return (LRESULT)infoPtr->hwndHeader;
9291 case LVM_GETHOTCURSOR:
9292 return infoPtr->hHotCursor;
9294 case LVM_GETHOTITEM:
9295 return infoPtr->nHotItem;
9297 case LVM_GETHOVERTIME:
9298 return infoPtr->dwHoverTime;
9300 case LVM_GETIMAGELIST:
9301 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9303 /* case LVN_GETINSERTMARK: */
9305 /* case LVN_GETINSERTMARKCOLOR: */
9307 /* case LVN_GETINSERTMARKRECT: */
9309 case LVM_GETISEARCHSTRINGA:
9310 case LVM_GETISEARCHSTRINGW:
9311 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9312 return FALSE;
9314 case LVM_GETITEMA:
9315 return LISTVIEW_GetItemT(infoPtr, (LPLVITEMW)lParam, FALSE, FALSE);
9317 case LVM_GETITEMW:
9318 return LISTVIEW_GetItemT(infoPtr, (LPLVITEMW)lParam, FALSE, TRUE);
9320 case LVM_GETITEMCOUNT:
9321 return GETITEMCOUNT(infoPtr);
9323 case LVM_GETITEMPOSITION:
9324 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9326 case LVM_GETITEMRECT:
9327 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9329 case LVM_GETITEMSPACING:
9330 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9332 case LVM_GETITEMSTATE:
9333 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9335 case LVM_GETITEMTEXTA:
9336 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9338 case LVM_GETITEMTEXTW:
9339 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9341 case LVM_GETNEXTITEM:
9342 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9344 case LVM_GETNUMBEROFWORKAREAS:
9345 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9346 return 1;
9348 case LVM_GETORIGIN:
9349 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9351 /* case LVN_GETOUTLINECOLOR: */
9353 /* case LVM_GETSELECTEDCOLUMN: */
9355 case LVM_GETSELECTEDCOUNT:
9356 return LISTVIEW_GetSelectedCount(infoPtr);
9358 case LVM_GETSELECTIONMARK:
9359 return infoPtr->nSelectionMark;
9361 case LVM_GETSTRINGWIDTHA:
9362 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9364 case LVM_GETSTRINGWIDTHW:
9365 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9367 case LVM_GETSUBITEMRECT:
9368 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, ((LPRECT)lParam)->top,
9369 ((LPRECT)lParam)->left, (LPRECT)lParam);
9371 case LVM_GETTEXTBKCOLOR:
9372 return LISTVIEW_GetTextBkColor(infoPtr);
9374 case LVM_GETTEXTCOLOR:
9375 return LISTVIEW_GetTextColor(infoPtr);
9377 /* case LVN_GETTILEINFO: */
9379 /* case LVN_GETTILEVIEWINFO: */
9381 case LVM_GETTOOLTIPS:
9382 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9383 return FALSE;
9385 case LVM_GETTOPINDEX:
9386 return LISTVIEW_GetTopIndex(infoPtr);
9388 /*case LVM_GETUNICODEFORMAT:
9389 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9390 return FALSE;*/
9392 case LVM_GETVIEWRECT:
9393 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9395 case LVM_GETWORKAREAS:
9396 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9397 return FALSE;
9399 /* case LVN_HASGROUP: */
9401 case LVM_HITTEST:
9402 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam);
9404 case LVM_INSERTCOLUMNA:
9405 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9407 case LVM_INSERTCOLUMNW:
9408 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9410 /* case LVN_INSERTGROUP: */
9412 /* case LVN_INSERTGROUPSORTED: */
9414 case LVM_INSERTITEMA:
9415 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9417 case LVM_INSERTITEMW:
9418 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9420 /* case LVN_INSERTMARKHITTEST: */
9422 /* case LVN_ISGROUPVIEWENABLED: */
9424 /* case LVN_MAPIDTOINDEX: */
9426 /* case LVN_INEDXTOID: */
9428 /* case LVN_MOVEGROUP: */
9430 /* case LVN_MOVEITEMTOGROUP: */
9432 case LVM_REDRAWITEMS:
9433 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9435 /* case LVN_REMOVEALLGROUPS: */
9437 /* case LVN_REMOVEGROUP: */
9439 case LVM_SCROLL:
9440 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9442 case LVM_SETBKCOLOR:
9443 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9445 /* case LVM_SETBKIMAGE: */
9447 case LVM_SETCALLBACKMASK:
9448 infoPtr->uCallbackMask = (UINT)wParam;
9449 return TRUE;
9451 case LVM_SETCOLUMNA:
9452 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9454 case LVM_SETCOLUMNW:
9455 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9457 case LVM_SETCOLUMNORDERARRAY:
9458 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9460 case LVM_SETCOLUMNWIDTH:
9461 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
9463 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9464 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9466 /* case LVN_SETGROUPINFO: */
9468 /* case LVN_SETGROUPMETRICS: */
9470 case LVM_SETHOTCURSOR:
9471 return LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9473 case LVM_SETHOTITEM:
9474 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9476 case LVM_SETHOVERTIME:
9477 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9479 case LVM_SETICONSPACING:
9480 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
9482 case LVM_SETIMAGELIST:
9483 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9485 /* case LVN_SETINFOTIP: */
9487 /* case LVN_SETINSERTMARK: */
9489 /* case LVN_SETINSERTMARKCOLOR: */
9491 case LVM_SETITEMA:
9492 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9494 case LVM_SETITEMW:
9495 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9497 case LVM_SETITEMCOUNT:
9498 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9500 case LVM_SETITEMPOSITION:
9501 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (INT)LOWORD(lParam),
9502 (INT)HIWORD(lParam));
9504 case LVM_SETITEMPOSITION32:
9505 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, ((POINT*)lParam)->x,
9506 ((POINT*)lParam)->y);
9508 case LVM_SETITEMSTATE:
9509 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9511 case LVM_SETITEMTEXTA:
9512 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9514 case LVM_SETITEMTEXTW:
9515 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9517 /* case LVN_SETOUTLINECOLOR: */
9519 /* case LVN_SETSELECTEDCOLUMN: */
9521 case LVM_SETSELECTIONMARK:
9522 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9524 case LVM_SETTEXTBKCOLOR:
9525 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9527 case LVM_SETTEXTCOLOR:
9528 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9530 /* case LVN_SETTILEINFO: */
9532 /* case LVN_SETTILEVIEWINFO: */
9534 /* case LVN_SETTILEWIDTH: */
9536 /* case LVM_SETTOOLTIPS: */
9538 /* case LVM_SETUNICODEFORMAT: */
9540 /* case LVN_SETVIEW: */
9542 /* case LVM_SETWORKAREAS: */
9544 /* case LVN_SORTGROUPS: */
9546 case LVM_SORTITEMS:
9547 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9549 case LVM_SUBITEMHITTEST:
9550 return LISTVIEW_SubItemHitTest(infoPtr, (LPLVHITTESTINFO)lParam);
9552 case LVM_UPDATE:
9553 return LISTVIEW_Update(infoPtr, (INT)wParam);
9555 case WM_CHAR:
9556 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9558 case WM_COMMAND:
9559 return LISTVIEW_Command(infoPtr, wParam, lParam);
9561 case WM_CREATE:
9562 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9564 case WM_ERASEBKGND:
9565 return LISTVIEW_EraseBackground(infoPtr, wParam, lParam);
9567 case WM_GETDLGCODE:
9568 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9570 case WM_GETFONT:
9571 return infoPtr->hFont;
9573 case WM_HSCROLL:
9574 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9576 case WM_KEYDOWN:
9577 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9579 case WM_KILLFOCUS:
9580 return LISTVIEW_KillFocus(infoPtr);
9582 case WM_LBUTTONDBLCLK:
9583 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9585 case WM_LBUTTONDOWN:
9586 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9588 case WM_LBUTTONUP:
9589 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9591 case WM_MOUSEMOVE:
9592 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9594 case WM_MOUSEHOVER:
9595 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9597 case WM_NCDESTROY:
9598 return LISTVIEW_NCDestroy(infoPtr);
9600 case WM_NOTIFY:
9601 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
9603 case WM_NOTIFYFORMAT:
9604 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9606 case WM_PAINT:
9607 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9609 case WM_RBUTTONDBLCLK:
9610 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9612 case WM_RBUTTONDOWN:
9613 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9615 case WM_RBUTTONUP:
9616 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9618 case WM_SETCURSOR:
9619 return LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam));
9621 case WM_SETFOCUS:
9622 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9624 case WM_SETFONT:
9625 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9627 case WM_SETREDRAW:
9628 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9630 case WM_SIZE:
9631 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9633 case WM_STYLECHANGED:
9634 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9636 case WM_SYSCOLORCHANGE:
9637 COMCTL32_RefreshSysColors();
9638 return 0;
9640 /* case WM_TIMER: */
9642 case WM_VSCROLL:
9643 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9645 case WM_MOUSEWHEEL:
9646 if (wParam & (MK_SHIFT | MK_CONTROL))
9647 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9648 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9650 case WM_WINDOWPOSCHANGED:
9651 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
9652 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9653 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9654 LISTVIEW_UpdateSize(infoPtr);
9655 LISTVIEW_UpdateScroll(infoPtr);
9657 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9659 /* case WM_WININICHANGE: */
9661 default:
9662 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9663 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9665 /* call default window procedure */
9666 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9669 return 0;
9672 /***
9673 * DESCRIPTION:
9674 * Registers the window class.
9676 * PARAMETER(S):
9677 * None
9679 * RETURN:
9680 * None
9682 VOID LISTVIEW_Register(void)
9684 WNDCLASSW wndClass;
9686 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9687 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9688 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9689 wndClass.cbClsExtra = 0;
9690 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9691 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9692 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9693 wndClass.lpszClassName = WC_LISTVIEWW;
9694 RegisterClassW(&wndClass);
9697 /***
9698 * DESCRIPTION:
9699 * Unregisters the window class.
9701 * PARAMETER(S):
9702 * None
9704 * RETURN:
9705 * None
9707 VOID LISTVIEW_Unregister(void)
9709 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9712 /***
9713 * DESCRIPTION:
9714 * Handle any WM_COMMAND messages
9716 * PARAMETER(S):
9718 * RETURN:
9720 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9722 switch (HIWORD(wParam))
9724 case EN_UPDATE:
9727 * Adjust the edit window size
9729 WCHAR buffer[1024];
9730 HDC hdc = GetDC(infoPtr->hwndEdit);
9731 HFONT hFont, hOldFont = 0;
9732 RECT rect;
9733 SIZE sz;
9734 int len;
9736 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9737 GetWindowRect(infoPtr->hwndEdit, &rect);
9739 /* Select font to get the right dimension of the string */
9740 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9741 if(hFont != 0)
9743 hOldFont = SelectObject(hdc, hFont);
9746 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9748 TEXTMETRICW textMetric;
9750 /* Add Extra spacing for the next character */
9751 GetTextMetricsW(hdc, &textMetric);
9752 sz.cx += (textMetric.tmMaxCharWidth * 2);
9754 SetWindowPos (
9755 infoPtr->hwndEdit,
9756 HWND_TOP,
9759 sz.cx,
9760 rect.bottom - rect.top,
9761 SWP_DRAWFRAME|SWP_NOMOVE);
9763 if(hFont != 0)
9764 SelectObject(hdc, hOldFont);
9766 ReleaseDC(infoPtr->hwndSelf, hdc);
9768 break;
9771 default:
9772 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
9775 return 0;
9779 /***
9780 * DESCRIPTION:
9781 * Subclassed edit control windproc function
9783 * PARAMETER(S):
9785 * RETURN:
9787 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9788 WPARAM wParam, LPARAM lParam, BOOL isW)
9790 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9791 static BOOL bIgnoreKillFocus = FALSE;
9792 BOOL cancel = FALSE;
9794 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9795 hwnd, uMsg, wParam, lParam, isW);
9797 switch (uMsg)
9799 case WM_GETDLGCODE:
9800 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9802 case WM_KILLFOCUS:
9803 if(bIgnoreKillFocus) return TRUE;
9804 break;
9806 case WM_DESTROY:
9808 WNDPROC editProc = infoPtr->EditWndProc;
9809 infoPtr->EditWndProc = 0;
9810 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9811 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9814 case WM_KEYDOWN:
9815 if (VK_ESCAPE == (INT)wParam)
9817 cancel = TRUE;
9818 break;
9820 else if (VK_RETURN == (INT)wParam)
9821 break;
9823 default:
9824 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9827 if (infoPtr->bEditing)
9829 LPWSTR buffer = NULL;
9831 if (!cancel)
9833 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9835 if (len)
9837 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9839 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9840 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9844 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9845 /* eg. Using a messagebox */
9846 bIgnoreKillFocus = TRUE;
9847 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9849 if (buffer) COMCTL32_Free(buffer);
9851 bIgnoreKillFocus = FALSE;
9854 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9855 return TRUE;
9858 /***
9859 * DESCRIPTION:
9860 * Subclassed edit control windproc function
9862 * PARAMETER(S):
9864 * RETURN:
9866 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9868 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9871 /***
9872 * DESCRIPTION:
9873 * Subclassed edit control windproc function
9875 * PARAMETER(S):
9877 * RETURN:
9879 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9881 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9884 /***
9885 * DESCRIPTION:
9886 * Creates a subclassed edit cotrol
9888 * PARAMETER(S):
9890 * RETURN:
9892 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9893 INT x, INT y, INT width, INT height, BOOL isW)
9895 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9896 HWND hedit;
9897 SIZE sz;
9898 HDC hdc;
9899 HDC hOldFont=0;
9900 TEXTMETRICW textMetric;
9901 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9903 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9905 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9906 hdc = GetDC(infoPtr->hwndSelf);
9908 /* Select the font to get appropriate metric dimensions */
9909 if(infoPtr->hFont != 0)
9910 hOldFont = SelectObject(hdc, infoPtr->hFont);
9912 /*Get String Lenght in pixels */
9913 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9915 /*Add Extra spacing for the next character */
9916 GetTextMetricsW(hdc, &textMetric);
9917 sz.cx += (textMetric.tmMaxCharWidth * 2);
9919 if(infoPtr->hFont != 0)
9920 SelectObject(hdc, hOldFont);
9922 ReleaseDC(infoPtr->hwndSelf, hdc);
9923 if (isW)
9924 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9925 else
9926 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9928 if (!hedit) return 0;
9930 infoPtr->EditWndProc = (WNDPROC)
9931 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9932 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9934 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9936 return hedit;