Fixed some issues found by winapi_check.
[wine/dcerpc.git] / dlls / comctl32 / listview.c
blob7bae9a1bbe0033b1cc4013bf302312a894081b67
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_GetHotCursor : not implemented
41 * LISTVIEW_GetISearchString : not implemented
42 * LISTVIEW_GetBkImage : not implemented
43 * LISTVIEW_SetBkImage : not implemented
44 * LISTVIEW_GetColumnOrderArray : simple hack only
45 * LISTVIEW_SetColumnOrderArray : simple hack only
46 * LISTVIEW_Arrange : empty stub
47 * LISTVIEW_ApproximateViewRect : incomplete
48 * LISTVIEW_Update : not completed
50 * Known differences in message stream from native control (not known if
51 * these differences cause problems):
52 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
53 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
54 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
55 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
56 * does *not* invoke DefWindowProc
57 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
58 * processing for "USEDOUBLECLICKTIME".
62 #include "config.h"
63 #include "wine/port.h"
65 #include <ctype.h>
66 #include <string.h>
67 #include <stdlib.h>
68 #include <stdio.h>
70 #include "winbase.h"
71 #include "winnt.h"
72 #include "heap.h"
73 #include "commctrl.h"
74 #include "comctl32.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(listview);
79 /* Some definitions for inline edit control */
80 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
81 typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
83 typedef struct tagLV_INTHIT
85 LVHITTESTINFO ht;
86 DWORD distance; /* distance to closest item */
87 INT iDistItem; /* item number that is closest */
88 } LV_INTHIT, *LPLV_INTHIT;
91 typedef struct tagEDITLABEL_ITEM
93 WNDPROC EditWndProc;
94 DWORD param;
95 EditlblCallbackW EditLblCb;
96 } EDITLABEL_ITEM;
98 typedef struct tagLISTVIEW_SUBITEM
100 LPWSTR pszText;
101 INT iImage;
102 INT iSubItem;
103 } LISTVIEW_SUBITEM;
105 typedef struct tagLISTVIEW_ITEM
107 UINT state;
108 LPWSTR pszText;
109 INT iImage;
110 LPARAM lParam;
111 INT iIndent;
112 POINT ptPosition;
114 } LISTVIEW_ITEM;
116 typedef struct tagLISTVIEW_SELECTION
118 DWORD lower;
119 DWORD upper;
120 } LISTVIEW_SELECTION;
122 typedef struct tagLISTVIEW_INFO
124 HWND hwndSelf;
125 COLORREF clrBk;
126 COLORREF clrText;
127 COLORREF clrTextBk;
128 HIMAGELIST himlNormal;
129 HIMAGELIST himlSmall;
130 HIMAGELIST himlState;
131 BOOL bLButtonDown;
132 BOOL bRButtonDown;
133 INT nFocusedItem;
134 HDPA hdpaSelectionRanges;
135 INT nItemHeight;
136 INT nItemWidth;
137 INT nSelectionMark;
138 INT nHotItem;
139 SHORT notifyFormat;
140 RECT rcList;
141 RECT rcView;
142 SIZE iconSize;
143 SIZE iconSpacing;
144 UINT uCallbackMask;
145 HWND hwndHeader;
146 HFONT hDefaultFont;
147 HFONT hFont;
148 INT ntmHeight; /* from GetTextMetrics from above font */
149 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
150 BOOL bFocus;
151 DWORD dwExStyle; /* extended listview style */
152 HDPA hdpaItems;
153 PFNLVCOMPARE pfnCompare;
154 LPARAM lParamSort;
155 HWND hwndEdit;
156 INT nEditLabelItem;
157 EDITLABEL_ITEM *pedititem;
158 DWORD dwHoverTime;
159 INT nColumnCount; /* the number of columns in this control */
161 DWORD lastKeyPressTimestamp; /* Added */
162 WPARAM charCode; /* Added */
163 INT nSearchParamLength; /* Added */
164 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
165 BOOL bIsDrawing;
166 } LISTVIEW_INFO;
169 * constants
172 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
173 #define SB_INTERNAL_UP -1
174 #define SB_INTERNAL_DOWN -2
175 #define SB_INTERNAL_RIGHT -3
176 #define SB_INTERNAL_LEFT -4
178 /* maximum size of a label */
179 #define DISP_TEXT_SIZE 512
181 /* padding for items in list and small icon display modes */
182 #define WIDTH_PADDING 12
184 /* padding for items in list, report and small icon display modes */
185 #define HEIGHT_PADDING 1
187 /* offset of items in report display mode */
188 #define REPORT_MARGINX 2
190 /* padding for icon in large icon display mode
191 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
192 * that HITTEST will see.
193 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
194 * ICON_TOP_PADDING - sum of the two above.
195 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
196 * LABEL_VERT_OFFSET - between bottom of text and end of box
198 #define ICON_TOP_PADDING_NOTHITABLE 2
199 #define ICON_TOP_PADDING_HITABLE 2
200 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
201 #define ICON_BOTTOM_PADDING 4
202 #define LABEL_VERT_OFFSET 10
204 /* default label width for items in list and small icon display modes */
205 #define DEFAULT_LABEL_WIDTH 40
207 /* default column width for items in list display mode */
208 #define DEFAULT_COLUMN_WIDTH 128
210 /* Size of "line" scroll for V & H scrolls */
211 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
213 /* Padding betwen image and label */
214 #define IMAGE_PADDING 2
216 /* Padding behind the label */
217 #define TRAILING_PADDING 5
219 /* Border for the icon caption */
220 #define CAPTION_BORDER 2
222 * macros
224 /* retrieve the number of items in the listview */
225 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
226 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
228 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
229 INT width, INT height, HWND parent, HINSTANCE hinst,
230 EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
233 * forward declarations
235 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
236 static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
237 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
238 static INT LISTVIEW_GetCountPerRow(HWND);
239 static INT LISTVIEW_GetCountPerColumn(HWND);
240 static VOID LISTVIEW_AlignLeft(HWND);
241 static VOID LISTVIEW_AlignTop(HWND);
242 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
243 static VOID LISTVIEW_AddSelection(HWND, INT);
244 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
245 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
246 static INT LISTVIEW_GetItemHeight(HWND);
247 static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
248 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
249 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
250 static LRESULT LISTVIEW_GetSubItemRect(HWND, INT, INT, INT, LPRECT);
251 static INT LISTVIEW_GetItemWidth(HWND);
252 static INT LISTVIEW_GetLabelWidth(HWND, INT);
253 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
254 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
255 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
256 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
257 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
258 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
259 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
260 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
261 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
262 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
263 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
264 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
265 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
266 static VOID LISTVIEW_UpdateScroll(HWND);
267 static VOID LISTVIEW_SetSelection(HWND, INT);
268 static BOOL LISTVIEW_UpdateSize(HWND);
269 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
270 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
271 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
272 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
273 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
274 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
275 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
276 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
277 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
278 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
279 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
280 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
281 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
282 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
283 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
284 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
285 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
286 static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
287 static LRESULT LISTVIEW_GetColumnT(HWND, INT, LPLVCOLUMNW, BOOL);
288 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos, HWND hScrollWnd);
289 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos, HWND hScrollWnd);
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 debugstr_t(LPCWSTR text, BOOL isW)
325 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
328 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
330 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
333 static inline LPCSTR lv_dmp_str(LPCWSTR text, BOOL isW, INT n)
335 return debugstr_tn(text, isW, min(n, textlenT(text, isW)));
338 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
340 LPWSTR wstr = (LPWSTR)text;
342 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
343 if (!isW && text)
345 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
346 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
347 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
349 TRACE(" wstr=%s\n", debugstr_w(wstr));
350 return wstr;
353 static inline void textfreeT(LPWSTR wstr, BOOL isW)
355 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
359 * dest is a pointer to a Unicode string
360 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
362 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
364 LPWSTR pszText = textdupTtoW(src, isW);
365 BOOL bResult = TRUE;
366 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
367 bResult = Str_SetPtrW(dest, pszText);
368 textfreeT(pszText, isW);
369 return bResult;
372 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
373 WPARAM wParam, LPARAM lParam, BOOL isW)
375 if (isW)
376 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
377 else
378 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
381 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
383 pnmh->hwndFrom = self;
384 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
385 pnmh->code = code;
386 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
387 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
390 static inline BOOL hdr_notify(HWND self, INT code)
392 NMHDR nmh;
393 return notify(self, code, &nmh);
396 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
398 return notify(self, code, (LPNMHDR)plvnm);
401 static int tabNotification[] = {
402 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
403 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
404 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
405 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
406 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
407 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
411 static int get_ansi_notification(INT unicodeNotificationCode)
413 int *pTabNotif = tabNotification;
414 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
415 if (*pTabNotif) return *pTabNotif;
416 ERR("unknown notification %x\n", unicodeNotificationCode);
417 return unicodeNotificationCode;
421 Send notification. depends on dispinfoW having same
422 structure as dispinfoA.
423 self : listview handle
424 notificationCode : *Unicode* notification code
425 pdi : dispinfo structure (can be unicode or ansi)
426 isW : TRUE if dispinfo is Unicode
428 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
430 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
431 BOOL bResult = FALSE;
432 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
433 INT realNotifCode;
434 INT cchTempBufMax = 0, savCchTextMax = 0;
435 LPWSTR pszTempBuf = NULL, savPszText = NULL;
437 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
438 TRACE(" notifyFormat=%s\n",
439 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
440 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
441 if (infoPtr->notifyFormat == NFR_ANSI)
442 realNotifCode = get_ansi_notification(notificationCode);
443 else
444 realNotifCode = notificationCode;
446 if (is_textT(pdi->item.pszText, isW))
448 if (isW && infoPtr->notifyFormat == NFR_ANSI)
449 convertToAnsi = TRUE;
450 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
451 convertToUnicode = TRUE;
454 if (convertToAnsi || convertToUnicode)
456 TRACE(" we have to convert the text to the correct format\n");
457 if (notificationCode != LVN_GETDISPINFOW)
458 { /* length of existing text */
459 cchTempBufMax = convertToUnicode ?
460 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
461 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
463 else
464 cchTempBufMax = pdi->item.cchTextMax;
466 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
467 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
468 if (!pszTempBuf) return FALSE;
469 if (convertToUnicode)
470 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
471 pszTempBuf, cchTempBufMax);
472 else
473 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
474 cchTempBufMax, NULL, NULL);
475 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
476 savCchTextMax = pdi->item.cchTextMax;
477 savPszText = pdi->item.pszText;
478 pdi->item.pszText = pszTempBuf;
479 pdi->item.cchTextMax = cchTempBufMax;
482 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
484 if (convertToUnicode || convertToAnsi)
485 { /* convert back result */
486 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
487 if (convertToUnicode) /* note : pointer can be changed by app ! */
488 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
489 savCchTextMax, NULL, NULL);
490 else
491 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
492 savPszText, savCchTextMax);
493 pdi->item.pszText = savPszText; /* restores our buffer */
494 pdi->item.cchTextMax = savCchTextMax;
495 HeapFree(GetProcessHeap(), 0, pszTempBuf);
497 return bResult;
500 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
502 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
505 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
507 int res;
509 n = min(min(n, strlenW(s1)), strlenW(s2));
510 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
511 return res ? res - 2 : res;
514 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
516 static int index = 0;
517 static char buffers[20][256];
518 char* buf = buffers[index++ % 20];
519 if (lpLVItem == NULL) return "(null)";
520 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
521 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
522 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
523 lpLVItem->state, lpLVItem->stateMask,
524 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
525 lv_dmp_str(lpLVItem->pszText, isW, 80),
526 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
527 lpLVItem->iIndent);
528 return buf;
531 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
533 static int index = 0;
534 static char buffers[20][256];
535 char* buf = buffers[index++ % 20];
536 if (lpColumn == NULL) return "(null)";
537 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
538 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
539 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
540 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
541 lv_dmp_str(lpColumn->pszText, isW, 80): "",
542 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
543 return buf;
546 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
548 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
549 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
550 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
551 iP->nItemHeight, iP->nItemWidth, dwStyle);
552 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
553 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
554 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
557 static BOOL
558 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
559 RECT rc)
561 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
562 NMLVCUSTOMDRAW nmcdhdr;
563 LPNMCUSTOMDRAW nmcd;
565 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
567 nmcd= & nmcdhdr.nmcd;
568 nmcd->hdr.hwndFrom = hwnd;
569 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
570 nmcd->hdr.code = NM_CUSTOMDRAW;
571 nmcd->dwDrawStage= dwDrawStage;
572 nmcd->hdc = hdc;
573 nmcd->rc.left = rc.left;
574 nmcd->rc.right = rc.right;
575 nmcd->rc.bottom = rc.bottom;
576 nmcd->rc.top = rc.top;
577 nmcd->dwItemSpec = 0;
578 nmcd->uItemState = 0;
579 nmcd->lItemlParam= 0;
580 nmcdhdr.clrText = infoPtr->clrText;
581 nmcdhdr.clrTextBk= infoPtr->clrBk;
583 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
584 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
587 static BOOL
588 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
589 UINT iItem, UINT iSubItem,
590 UINT uItemDrawState)
592 LISTVIEW_INFO *infoPtr;
593 NMLVCUSTOMDRAW nmcdhdr;
594 LPNMCUSTOMDRAW nmcd;
595 DWORD dwDrawStage,dwItemSpec;
596 UINT uItemState;
597 INT retval;
598 RECT itemRect;
599 LVITEMW item;
601 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
603 ZeroMemory(&item,sizeof(item));
604 item.iItem = iItem;
605 item.mask = LVIF_PARAM;
606 ListView_GetItemW(hwnd,&item);
608 dwDrawStage=CDDS_ITEM | uItemDrawState;
609 dwItemSpec=iItem;
610 uItemState=0;
612 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
613 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
614 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
616 itemRect.left = LVIR_BOUNDS;
617 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
619 nmcd= & nmcdhdr.nmcd;
620 nmcd->hdr.hwndFrom = hwnd;
621 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
622 nmcd->hdr.code = NM_CUSTOMDRAW;
623 nmcd->dwDrawStage= dwDrawStage;
624 nmcd->hdc = hdc;
625 nmcd->rc.left = itemRect.left;
626 nmcd->rc.right = itemRect.right;
627 nmcd->rc.bottom = itemRect.bottom;
628 nmcd->rc.top = itemRect.top;
629 nmcd->dwItemSpec = dwItemSpec;
630 nmcd->uItemState = uItemState;
631 nmcd->lItemlParam= item.lParam;
632 nmcdhdr.clrText = infoPtr->clrText;
633 nmcdhdr.clrTextBk= infoPtr->clrBk;
634 nmcdhdr.iSubItem =iSubItem;
636 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
637 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
638 nmcd->uItemState, nmcd->lItemlParam);
640 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
641 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
643 infoPtr->clrText=nmcdhdr.clrText;
644 infoPtr->clrBk =nmcdhdr.clrTextBk;
645 return (BOOL) retval;
649 /*************************************************************************
650 * LISTVIEW_ProcessLetterKeys
652 * Processes keyboard messages generated by pressing the letter keys
653 * on the keyboard.
654 * What this does is perform a case insensitive search from the
655 * current position with the following quirks:
656 * - If two chars or more are pressed in quick succession we search
657 * for the corresponding string (e.g. 'abc').
658 * - If there is a delay we wipe away the current search string and
659 * restart with just that char.
660 * - If the user keeps pressing the same character, whether slowly or
661 * fast, so that the search string is entirely composed of this
662 * character ('aaaaa' for instance), then we search for first item
663 * that starting with that character.
664 * - If the user types the above character in quick succession, then
665 * we must also search for the corresponding string ('aaaaa'), and
666 * go to that string if there is a match.
668 * RETURNS
670 * Zero.
672 * BUGS
674 * - The current implementation has a list of characters it will
675 * accept and it ignores averything else. In particular it will
676 * ignore accentuated characters which seems to match what
677 * Windows does. But I'm not sure it makes sense to follow
678 * Windows there.
679 * - We don't sound a beep when the search fails.
681 * SEE ALSO
683 * TREEVIEW_ProcessLetterKeys
685 static INT LISTVIEW_ProcessLetterKeys(
686 HWND hwnd, /* handle to the window */
687 WPARAM charCode, /* the character code, the actual character */
688 LPARAM keyData /* key data */
691 LISTVIEW_INFO *infoPtr;
692 INT nItem;
693 INT nSize;
694 INT endidx,idx;
695 LVITEMW item;
696 WCHAR buffer[MAX_PATH];
697 DWORD timestamp,elapsed;
699 /* simple parameter checking */
700 if (!hwnd || !charCode || !keyData)
701 return 0;
703 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
704 if (!infoPtr)
705 return 0;
707 /* only allow the valid WM_CHARs through */
708 if (!isalnum(charCode) &&
709 charCode != '.' && charCode != '`' && charCode != '!' &&
710 charCode != '@' && charCode != '#' && charCode != '$' &&
711 charCode != '%' && charCode != '^' && charCode != '&' &&
712 charCode != '*' && charCode != '(' && charCode != ')' &&
713 charCode != '-' && charCode != '_' && charCode != '+' &&
714 charCode != '=' && charCode != '\\'&& charCode != ']' &&
715 charCode != '}' && charCode != '[' && charCode != '{' &&
716 charCode != '/' && charCode != '?' && charCode != '>' &&
717 charCode != '<' && charCode != ',' && charCode != '~')
718 return 0;
720 nSize=GETITEMCOUNT(infoPtr);
721 /* if there's one item or less, there is no where to go */
722 if (nSize <= 1)
723 return 0;
725 /* compute how much time elapsed since last keypress */
726 timestamp=GetTickCount();
727 if (timestamp > infoPtr->lastKeyPressTimestamp) {
728 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
729 } else {
730 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
733 /* update the search parameters */
734 infoPtr->lastKeyPressTimestamp=timestamp;
735 if (elapsed < KEY_DELAY) {
736 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
737 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
739 if (infoPtr->charCode != charCode) {
740 infoPtr->charCode=charCode=0;
742 } else {
743 infoPtr->charCode=charCode;
744 infoPtr->szSearchParam[0]=charCode;
745 infoPtr->nSearchParamLength=1;
746 /* Redundant with the 1 char string */
747 charCode=0;
750 /* and search from the current position */
751 nItem=-1;
752 if (infoPtr->nFocusedItem >= 0) {
753 endidx=infoPtr->nFocusedItem;
754 idx=endidx;
755 /* if looking for single character match,
756 * then we must always move forward
758 if (infoPtr->nSearchParamLength == 1)
759 idx++;
760 } else {
761 endidx=nSize;
762 idx=0;
764 do {
765 if (idx == nSize) {
766 if (endidx == nSize)
767 break;
768 idx=0;
771 /* get item */
772 ZeroMemory(&item, sizeof(item));
773 item.mask = LVIF_TEXT;
774 item.iItem = idx;
775 item.iSubItem = 0;
776 item.pszText = buffer;
777 item.cchTextMax = COUNTOF(buffer);
778 ListView_GetItemW( hwnd, &item );
780 /* check for a match */
781 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
782 nItem=idx;
783 break;
784 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
785 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
786 /* This would work but we must keep looking for a longer match */
787 nItem=idx;
789 idx++;
790 } while (idx != endidx);
792 if (nItem != -1) {
793 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
794 /* refresh client area */
795 InvalidateRect(hwnd, NULL, TRUE);
796 UpdateWindow(hwnd);
800 return 0;
803 /*************************************************************************
804 * LISTVIEW_UpdateHeaderSize [Internal]
806 * Function to resize the header control
808 * PARAMS
809 * hwnd [I] handle to a window
810 * nNewScrollPos [I] Scroll Pos to Set
812 * RETURNS
813 * nothing
815 * NOTES
817 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
819 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
820 RECT winRect;
821 POINT point[2];
823 GetWindowRect(infoPtr->hwndHeader, &winRect);
824 point[0].x = winRect.left;
825 point[0].y = winRect.top;
826 point[1].x = winRect.right;
827 point[1].y = winRect.bottom;
829 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
830 point[0].x = -nNewScrollPos;
831 point[1].x += nNewScrollPos;
833 SetWindowPos(infoPtr->hwndHeader,0,
834 point[0].x,point[0].y,point[1].x,point[1].y,
835 SWP_NOZORDER | SWP_NOACTIVATE);
838 /***
839 * DESCRIPTION:
840 * Update the scrollbars. This functions should be called whenever
841 * the content, size or view changes.
843 * PARAMETER(S):
844 * [I] HWND : window handle
846 * RETURN:
847 * None
849 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
851 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
852 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
853 UINT uView = lStyle & LVS_TYPEMASK;
854 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
855 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
856 SCROLLINFO scrollInfo;
858 if (lStyle & LVS_NOSCROLL) return;
860 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
861 scrollInfo.cbSize = sizeof(SCROLLINFO);
863 if (uView == LVS_LIST)
865 /* update horizontal scrollbar */
867 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
868 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
869 INT nNumOfItems = GETITEMCOUNT(infoPtr);
871 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
872 TRACE("items=%d, perColumn=%d, perRow=%d\n",
873 nNumOfItems, nCountPerColumn, nCountPerRow);
874 if((nNumOfItems % nCountPerColumn) == 0)
876 scrollInfo.nMax--;
878 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
879 scrollInfo.nPage = nCountPerRow;
880 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
881 TRACE("LVS_LIST\n");
882 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
883 ShowScrollBar(hwnd, SB_VERT, FALSE);
885 else if (uView == LVS_REPORT)
887 BOOL test;
889 /* update vertical scrollbar */
890 scrollInfo.nMin = 0;
891 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
892 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
893 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
894 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
895 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
896 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
897 scrollInfo.nMax, scrollInfo.nPage, test);
898 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
899 ShowScrollBar(hwnd, SB_VERT, (test) ? FALSE : TRUE);
901 /* update horizontal scrollbar */
902 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
903 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
904 || GETITEMCOUNT(infoPtr) == 0)
906 scrollInfo.nPos = 0;
908 scrollInfo.nMin = 0;
909 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
910 scrollInfo.nPage = nListWidth;
911 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
912 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
913 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
914 scrollInfo.nMax, scrollInfo.nPage, test);
915 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
916 ShowScrollBar(hwnd, SB_HORZ, (test) ? FALSE : TRUE);
918 /* Update the Header Control */
919 scrollInfo.fMask = SIF_POS;
920 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
921 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
924 else
926 RECT rcView;
928 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
930 INT nViewWidth = rcView.right - rcView.left;
931 INT nViewHeight = rcView.bottom - rcView.top;
933 /* Update Horizontal Scrollbar */
934 scrollInfo.fMask = SIF_POS;
935 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
936 || GETITEMCOUNT(infoPtr) == 0)
938 scrollInfo.nPos = 0;
940 scrollInfo.nMax = max(nViewWidth, 0)-1;
941 scrollInfo.nMin = 0;
942 scrollInfo.nPage = nListWidth;
943 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
944 TRACE("LVS_ICON/SMALLICON Horz.\n");
945 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
947 /* Update Vertical Scrollbar */
948 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
949 scrollInfo.fMask = SIF_POS;
950 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
951 || GETITEMCOUNT(infoPtr) == 0)
953 scrollInfo.nPos = 0;
955 scrollInfo.nMax = max(nViewHeight,0)-1;
956 scrollInfo.nMin = 0;
957 scrollInfo.nPage = nListHeight;
958 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
959 TRACE("LVS_ICON/SMALLICON Vert.\n");
960 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
965 /***
966 * DESCRIPTION:
967 * Prints a message for unsupported window styles.
968 * A kind of TODO list for window styles.
970 * PARAMETER(S):
971 * [I] LONG : window style
973 * RETURN:
974 * None
976 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
978 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
979 FIXME(" LVS_NOSCROLL\n");
981 if (lStyle & LVS_NOLABELWRAP)
982 FIXME(" LVS_NOLABELWRAP\n");
984 if (!(lStyle & LVS_SHAREIMAGELISTS))
985 FIXME(" !LVS_SHAREIMAGELISTS\n");
987 if (lStyle & LVS_SORTASCENDING)
988 FIXME(" LVS_SORTASCENDING\n");
990 if (lStyle & LVS_SORTDESCENDING)
991 FIXME(" LVS_SORTDESCENDING\n");
994 /***
995 * DESCRIPTION:
996 * Aligns the items with the top edge of the window.
998 * PARAMETER(S):
999 * [I] HWND : window handle
1001 * RETURN:
1002 * None
1004 static VOID LISTVIEW_AlignTop(HWND hwnd)
1006 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1007 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1008 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1009 POINT ptItem;
1010 RECT rcView;
1011 INT i, off_x=0, off_y=0;
1013 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1015 /* Since SetItemPosition uses upper-left of icon, and for
1016 style=LVS_ICON the icon is not left adjusted, get the offset */
1017 if (uView == LVS_ICON)
1019 off_y = ICON_TOP_PADDING;
1020 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1022 ptItem.x = off_x;
1023 ptItem.y = off_y;
1024 ZeroMemory(&rcView, sizeof(RECT));
1025 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1026 off_x, off_y,
1027 infoPtr->rcList.left, infoPtr->rcList.right);
1029 if (nListWidth > infoPtr->nItemWidth)
1031 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1033 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1035 ptItem.x = off_x;
1036 ptItem.y += infoPtr->nItemHeight;
1039 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1040 ptItem.x += infoPtr->nItemWidth;
1041 rcView.right = max(rcView.right, ptItem.x);
1044 rcView.right -= off_x;
1045 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1047 else
1049 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1051 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1052 ptItem.y += infoPtr->nItemHeight;
1055 rcView.right = infoPtr->nItemWidth;
1056 rcView.bottom = ptItem.y-off_y;
1059 LISTVIEW_SetViewRect(hwnd, &rcView);
1063 /***
1064 * DESCRIPTION:
1065 * Aligns the items with the left edge of the window.
1067 * PARAMETER(S):
1068 * [I] HWND : window handle
1070 * RETURN:
1071 * None
1073 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1075 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1076 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1077 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1078 POINT ptItem;
1079 RECT rcView;
1080 INT i, off_x=0, off_y=0;
1082 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1084 /* Since SetItemPosition uses upper-left of icon, and for
1085 style=LVS_ICON the icon is not left adjusted, get the offset */
1086 if (uView == LVS_ICON)
1088 off_y = ICON_TOP_PADDING;
1089 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1091 ptItem.x = off_x;
1092 ptItem.y = off_y;
1093 ZeroMemory(&rcView, sizeof(RECT));
1094 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1096 if (nListHeight > infoPtr->nItemHeight)
1098 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1100 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1102 ptItem.y = off_y;
1103 ptItem.x += infoPtr->nItemWidth;
1106 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1107 ptItem.y += infoPtr->nItemHeight;
1108 rcView.bottom = max(rcView.bottom, ptItem.y);
1111 rcView.right = ptItem.x + infoPtr->nItemWidth;
1113 else
1115 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1117 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1118 ptItem.x += infoPtr->nItemWidth;
1121 rcView.bottom = infoPtr->nItemHeight;
1122 rcView.right = ptItem.x;
1125 LISTVIEW_SetViewRect(hwnd, &rcView);
1129 /***
1130 * DESCRIPTION:
1131 * Set the bounding rectangle of all the items.
1133 * PARAMETER(S):
1134 * [I] HWND : window handle
1135 * [I] LPRECT : bounding rectangle
1137 * RETURN:
1138 * SUCCESS : TRUE
1139 * FAILURE : FALSE
1141 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1143 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1144 BOOL bResult = FALSE;
1146 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1147 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1149 if (lprcView != NULL)
1151 bResult = TRUE;
1152 infoPtr->rcView.left = lprcView->left;
1153 infoPtr->rcView.top = lprcView->top;
1154 infoPtr->rcView.right = lprcView->right;
1155 infoPtr->rcView.bottom = lprcView->bottom;
1158 return bResult;
1161 /***
1162 * DESCRIPTION:
1163 * Retrieves the bounding rectangle of all the items.
1165 * PARAMETER(S):
1166 * [I] HWND : window handle
1167 * [O] LPRECT : bounding rectangle
1169 * RETURN:
1170 * SUCCESS : TRUE
1171 * FAILURE : FALSE
1173 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1175 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1176 BOOL bResult = FALSE;
1177 POINT ptOrigin;
1179 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1181 if (lprcView != NULL)
1183 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1184 if (bResult != FALSE)
1186 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1187 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1188 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1189 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1192 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1193 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1196 return bResult;
1199 /***
1200 * DESCRIPTION:
1201 * Retrieves the subitem pointer associated with the subitem index.
1203 * PARAMETER(S):
1204 * [I] HDPA : DPA handle for a specific item
1205 * [I] INT : index of subitem
1207 * RETURN:
1208 * SUCCESS : subitem pointer
1209 * FAILURE : NULL
1211 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1212 INT nSubItem)
1214 LISTVIEW_SUBITEM *lpSubItem;
1215 INT i;
1217 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1219 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1220 if (lpSubItem != NULL)
1222 if (lpSubItem->iSubItem == nSubItem)
1224 return lpSubItem;
1229 return NULL;
1232 /***
1233 * DESCRIPTION:
1234 * Calculates the width of an item.
1236 * PARAMETER(S):
1237 * [I] HWND : window handle
1238 * [I] LONG : window style
1240 * RETURN:
1241 * Returns item width.
1243 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1245 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1246 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1247 UINT uView = style & LVS_TYPEMASK;
1248 INT nHeaderItemCount;
1249 RECT rcHeaderItem;
1250 INT nItemWidth = 0;
1251 INT nLabelWidth;
1252 INT i;
1254 TRACE("(hwnd=%x)\n", hwnd);
1256 if (uView == LVS_ICON)
1258 nItemWidth = infoPtr->iconSpacing.cx;
1260 else if (uView == LVS_REPORT)
1262 /* calculate width of header */
1263 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1264 for (i = 0; i < nHeaderItemCount; i++)
1266 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1268 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1272 else /* for LVS_SMALLICON and LVS_LIST */
1274 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1276 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1277 nItemWidth = max(nItemWidth, nLabelWidth);
1280 /* default label size */
1281 if (GETITEMCOUNT(infoPtr) == 0)
1283 nItemWidth = DEFAULT_COLUMN_WIDTH;
1285 else
1287 if (nItemWidth == 0)
1289 nItemWidth = DEFAULT_LABEL_WIDTH;
1291 else
1293 /* add padding */
1294 nItemWidth += WIDTH_PADDING;
1296 if (infoPtr->himlSmall != NULL)
1298 nItemWidth += infoPtr->iconSize.cx;
1301 if (infoPtr->himlState != NULL)
1303 nItemWidth += infoPtr->iconSize.cx;
1305 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1309 if(nItemWidth == 0)
1311 /* nItemWidth Cannot be Zero */
1312 nItemWidth = 1;
1314 return nItemWidth;
1317 /***
1318 * DESCRIPTION:
1319 * Calculates the width of a specific item.
1321 * PARAMETER(S):
1322 * [I] HWND : window handle
1323 * [I] LPSTR : string
1325 * RETURN:
1326 * Returns the width of an item width a specified string.
1328 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1330 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1331 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1332 INT nHeaderItemCount;
1333 RECT rcHeaderItem;
1334 INT nItemWidth = 0;
1335 INT i;
1337 TRACE("(hwnd=%x)\n", hwnd);
1339 if (uView == LVS_ICON)
1341 nItemWidth = infoPtr->iconSpacing.cx;
1343 else if (uView == LVS_REPORT)
1345 /* calculate width of header */
1346 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1347 for (i = 0; i < nHeaderItemCount; i++)
1349 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1351 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1355 else
1357 /* get width of string */
1358 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1360 /* default label size */
1361 if (GETITEMCOUNT(infoPtr) == 0)
1363 nItemWidth = DEFAULT_COLUMN_WIDTH;
1365 else
1367 if (nItemWidth == 0)
1369 nItemWidth = DEFAULT_LABEL_WIDTH;
1371 else
1373 /* add padding */
1374 nItemWidth += WIDTH_PADDING;
1376 if (infoPtr->himlSmall != NULL)
1378 nItemWidth += infoPtr->iconSize.cx;
1381 if (infoPtr->himlState != NULL)
1383 nItemWidth += infoPtr->iconSize.cx;
1389 return nItemWidth;
1392 /***
1393 * DESCRIPTION:
1394 * Retrieves and saves important text metrics info for the current
1395 * Listview font.
1397 * PARAMETER(S):
1398 * [I] HWND : window handle
1401 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1403 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1404 TEXTMETRICW tm;
1405 HDC hdc = GetDC(hwnd);
1406 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1407 INT oldHeight, oldACW;
1409 GetTextMetricsW(hdc, &tm);
1411 oldHeight = infoPtr->ntmHeight;
1412 oldACW = infoPtr->ntmAveCharWidth;
1413 infoPtr->ntmHeight = tm.tmHeight;
1414 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1416 SelectObject(hdc, hOldFont);
1417 ReleaseDC(hwnd, hdc);
1418 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1419 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1423 /***
1424 * DESCRIPTION:
1425 * Calculates the height of an item.
1427 * PARAMETER(S):
1428 * [I] HWND : window handle
1430 * RETURN:
1431 * Returns item height.
1433 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1435 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1436 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1437 INT nItemHeight = 0;
1439 if (uView == LVS_ICON)
1441 nItemHeight = infoPtr->iconSpacing.cy;
1443 else
1445 if(infoPtr->himlState || infoPtr->himlSmall)
1446 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1447 else
1448 nItemHeight = infoPtr->ntmHeight;
1451 return nItemHeight;
1455 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1457 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1458 LISTVIEW_SELECTION *selection;
1459 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1460 INT i;
1462 TRACE("Selections are:\n");
1463 for (i = 0; i < topSelection; i++)
1465 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1466 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1470 /***
1471 * DESCRIPTION:
1472 * A compare function for selection ranges
1474 *PARAMETER(S)
1475 * [I] LPVOID : Item 1;
1476 * [I] LPVOID : Item 2;
1477 * [I] LPARAM : flags
1479 *RETURNS:
1480 * >0 : if Item 1 > Item 2
1481 * <0 : if Item 2 > Item 1
1482 * 0 : if Item 1 == Item 2
1484 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1485 LPARAM flags)
1487 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1488 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1489 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1490 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1491 int rc=0;
1493 if (u1 < l2)
1494 rc= -1;
1496 if (u2 < l1)
1497 rc= 1;
1499 return rc;
1503 * DESCRIPTION:
1504 * Adds a selection range.
1506 * PARAMETER(S):
1507 * [I] HWND : window handle
1508 * [I] INT : lower item index
1509 * [I] INT : upper item index
1511 * RETURN:
1512 * None
1514 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1516 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1517 LISTVIEW_SELECTION *selection;
1518 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1519 BOOL lowerzero=FALSE;
1521 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1522 selection->lower = lItem;
1523 selection->upper = uItem;
1525 TRACE("Add range %i - %i\n", lItem, uItem);
1526 if (topSelection)
1528 LISTVIEW_SELECTION *checkselection,*checkselection2;
1529 INT index,mergeindex;
1531 /* find overlapping selections */
1532 /* we want to catch adjacent ranges so expand our range by 1 */
1534 selection->upper++;
1535 if (selection->lower == 0)
1536 lowerzero = TRUE;
1537 else
1538 selection->lower--;
1540 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1541 LISTVIEW_CompareSelectionRanges,
1542 0,0);
1543 selection->upper --;
1544 if (lowerzero)
1545 lowerzero=FALSE;
1546 else
1547 selection->lower ++;
1549 if (index >=0)
1551 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1552 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1553 checkselection->upper);
1555 checkselection->lower = min(selection->lower,checkselection->lower);
1556 checkselection->upper = max(selection->upper,checkselection->upper);
1558 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1559 checkselection->upper);
1561 COMCTL32_Free(selection);
1563 /* merge now common selection ranges in the lower group*/
1566 checkselection->upper ++;
1567 if (checkselection->lower == 0)
1568 lowerzero = TRUE;
1569 else
1570 checkselection->lower --;
1572 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1573 checkselection->upper);
1575 /* not sorted yet */
1576 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1577 LISTVIEW_CompareSelectionRanges, 0,
1580 checkselection->upper --;
1581 if (lowerzero)
1582 lowerzero = FALSE;
1583 else
1584 checkselection->lower ++;
1586 if (mergeindex >=0 && mergeindex != index)
1588 TRACE("Merge with index %i\n",mergeindex);
1589 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1590 mergeindex);
1591 checkselection->lower = min(checkselection->lower,
1592 checkselection2->lower);
1593 checkselection->upper = max(checkselection->upper,
1594 checkselection2->upper);
1595 COMCTL32_Free(checkselection2);
1596 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1597 index --;
1600 while (mergeindex > -1 && mergeindex <index);
1602 /* merge now common selection ranges in the upper group*/
1605 checkselection->upper ++;
1606 if (checkselection->lower == 0)
1607 lowerzero = TRUE;
1608 else
1609 checkselection->lower --;
1611 TRACE("search upper range %i (%lu - %lu)\n",index,
1612 checkselection->lower, checkselection->upper);
1614 /* not sorted yet */
1615 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1616 index+1,
1617 LISTVIEW_CompareSelectionRanges, 0,
1620 checkselection->upper --;
1621 if (lowerzero)
1622 lowerzero = FALSE;
1623 else
1624 checkselection->lower ++;
1626 if (mergeindex >=0 && mergeindex !=index)
1628 TRACE("Merge with index %i\n",mergeindex);
1629 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1630 mergeindex);
1631 checkselection->lower = min(checkselection->lower,
1632 checkselection2->lower);
1633 checkselection->upper = max(checkselection->upper,
1634 checkselection2->upper);
1635 COMCTL32_Free(checkselection2);
1636 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1639 while (mergeindex > -1);
1641 else
1644 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1645 LISTVIEW_CompareSelectionRanges, 0,
1646 DPAS_INSERTAFTER);
1648 TRACE("Insert before index %i\n",index);
1649 if (index == -1)
1650 index = 0;
1651 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1654 else
1656 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1659 * Incase of error
1661 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1662 LISTVIEW_PrintSelectionRanges(hwnd);
1666 * DESCRIPTION:
1667 * check if a specified index is selected.
1669 * PARAMETER(S):
1670 * [I] HWND : window handle
1671 * [I] INT : item index
1673 * RETURN:
1674 * None
1676 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1678 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1679 LISTVIEW_SELECTION selection;
1680 INT index;
1682 selection.upper = nItem;
1683 selection.lower = nItem;
1685 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1686 LISTVIEW_CompareSelectionRanges,
1687 0,DPAS_SORTED);
1688 if (index != -1)
1689 return TRUE;
1690 else
1691 return FALSE;
1694 /***
1695 * DESCRIPTION:
1696 * Removes all selection ranges
1698 * Parameters(s):
1699 * HWND: window handle
1701 * RETURNS:
1702 * SUCCESS : TRUE
1703 * FAILURE : TRUE
1705 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1707 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1708 LISTVIEW_SELECTION *selection;
1709 INT i;
1710 LVITEMW item;
1712 TRACE("(0x%x)\n",hwnd);
1714 ZeroMemory(&item,sizeof(item));
1715 item.stateMask = LVIS_SELECTED;
1719 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1720 if (selection)
1722 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1723 for (i = selection->lower; i<=selection->upper; i++)
1724 LISTVIEW_SetItemState(hwnd,i,&item);
1725 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1728 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1730 TRACE("done\n");
1731 return TRUE;
1735 * DESCRIPTION:
1736 * Removes a range selections.
1738 * PARAMETER(S):
1739 * [I] HWND : window handle
1740 * [I] INT : lower item index
1741 * [I] INT : upper item index
1743 * RETURN:
1744 * None
1746 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1748 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1749 LISTVIEW_SELECTION removeselection,*checkselection;
1750 INT index;
1752 removeselection.lower = lItem;
1753 removeselection.upper = uItem;
1755 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1756 LISTVIEW_PrintSelectionRanges(hwnd);
1758 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1759 LISTVIEW_CompareSelectionRanges,
1760 0,0);
1762 if (index == -1)
1763 return;
1766 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1767 index);
1769 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1770 checkselection->upper);
1772 /* case 1: Same */
1773 if ((checkselection->upper == removeselection.upper) &&
1774 (checkselection->lower == removeselection.lower))
1776 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1777 TRACE("Case 1\n");
1779 /* case 2: engulf */
1780 else if (((checkselection->upper < removeselection.upper) &&
1781 (checkselection->lower > removeselection.lower))||
1782 ((checkselection->upper <= removeselection.upper) &&
1783 (checkselection->lower > removeselection.lower)) ||
1784 ((checkselection->upper < removeselection.upper) &&
1785 (checkselection->lower >= removeselection.lower)))
1788 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1789 /* do it again because others may also get caught */
1790 TRACE("Case 2\n");
1791 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1793 /* case 3: overlap upper */
1794 else if ((checkselection->upper < removeselection.upper) &&
1795 (checkselection->lower < removeselection.lower))
1797 checkselection->upper = removeselection.lower - 1;
1798 TRACE("Case 3\n");
1799 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1801 /* case 4: overlap lower */
1802 else if ((checkselection->upper > removeselection.upper) &&
1803 (checkselection->lower > removeselection.lower))
1805 checkselection->lower = removeselection.upper + 1;
1806 TRACE("Case 4\n");
1807 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1809 /* case 5: fully internal */
1810 else if (checkselection->upper == removeselection.upper)
1811 checkselection->upper = removeselection.lower - 1;
1812 else if (checkselection->lower == removeselection.lower)
1813 checkselection->lower = removeselection.upper + 1;
1814 else
1816 /* bisect the range */
1817 LISTVIEW_SELECTION *newselection;
1819 newselection = (LISTVIEW_SELECTION *)
1820 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1821 newselection -> lower = checkselection->lower;
1822 newselection -> upper = removeselection.lower - 1;
1823 checkselection -> lower = removeselection.upper + 1;
1824 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1825 TRACE("Case 5\n");
1826 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1828 LISTVIEW_PrintSelectionRanges(hwnd);
1832 * DESCRIPTION:
1833 * Updates the various indices after an item has been inserted or deleted.
1835 * PARAMETER(S):
1836 * [I] HWND : window handle
1837 * [I] INT : item index
1838 * [I] INT : Direction of shift, +1 or -1.
1840 * RETURN:
1841 * None
1843 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1845 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1846 LISTVIEW_SELECTION selection,*checkselection;
1847 INT index;
1849 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1851 selection.upper = nItem;
1852 selection.lower = nItem;
1854 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1855 LISTVIEW_CompareSelectionRanges,
1856 0,DPAS_SORTED|DPAS_INSERTAFTER);
1858 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1860 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1861 if ((checkselection->lower >= nItem)&&
1862 ((int)(checkselection->lower + direction) >= 0))
1863 checkselection->lower += direction;
1864 if ((checkselection->upper >= nItem)&&
1865 ((int)(checkselection->upper + direction) >= 0))
1866 checkselection->upper += direction;
1867 index ++;
1870 /* Note that the following will fail if direction != +1 and -1 */
1871 if (infoPtr->nSelectionMark > nItem)
1872 infoPtr->nSelectionMark += direction;
1873 else if (infoPtr->nSelectionMark == nItem)
1875 if (direction > 0)
1876 infoPtr->nSelectionMark += direction;
1877 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1878 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1881 if (infoPtr->nFocusedItem > nItem)
1882 infoPtr->nFocusedItem += direction;
1883 else if (infoPtr->nFocusedItem == nItem)
1885 if (direction > 0)
1886 infoPtr->nFocusedItem += direction;
1887 else
1889 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1890 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1891 if (infoPtr->nFocusedItem >= 0)
1892 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1895 /* But we are not supposed to modify nHotItem! */
1900 * DESCRIPTION:
1901 * Adds a block of selections.
1903 * PARAMETER(S):
1904 * [I] HWND : window handle
1905 * [I] INT : item index
1907 * RETURN:
1908 * None
1910 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1912 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1913 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1914 INT nLast = max(infoPtr->nSelectionMark, nItem);
1915 INT i;
1916 LVITEMW item;
1918 if (nFirst == -1)
1919 nFirst = nItem;
1921 ZeroMemory(&item,sizeof(item));
1922 item.stateMask = LVIS_SELECTED;
1923 item.state = LVIS_SELECTED;
1925 for (i = nFirst; i <= nLast; i++)
1926 LISTVIEW_SetItemState(hwnd,i,&item);
1928 LISTVIEW_SetItemFocus(hwnd, nItem);
1929 infoPtr->nSelectionMark = nItem;
1933 /***
1934 * DESCRIPTION:
1935 * Adds a single selection.
1937 * PARAMETER(S):
1938 * [I] HWND : window handle
1939 * [I] INT : item index
1941 * RETURN:
1942 * None
1944 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1946 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1947 LVITEMW item;
1949 ZeroMemory(&item,sizeof(item));
1950 item.state = LVIS_SELECTED;
1951 item.stateMask = LVIS_SELECTED;
1953 LISTVIEW_SetItemState(hwnd,nItem,&item);
1955 LISTVIEW_SetItemFocus(hwnd, nItem);
1956 infoPtr->nSelectionMark = nItem;
1959 /***
1960 * DESCRIPTION:
1961 * Selects or unselects an item.
1963 * PARAMETER(S):
1964 * [I] HWND : window handle
1965 * [I] INT : item index
1967 * RETURN:
1968 * SELECT: TRUE
1969 * UNSELECT : FALSE
1971 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1973 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1974 BOOL bResult;
1975 LVITEMW item;
1977 ZeroMemory(&item,sizeof(item));
1978 item.stateMask = LVIS_SELECTED;
1980 if (LISTVIEW_IsSelected(hwnd,nItem))
1982 LISTVIEW_SetItemState(hwnd,nItem,&item);
1983 bResult = FALSE;
1985 else
1987 item.state = LVIS_SELECTED;
1988 LISTVIEW_SetItemState(hwnd,nItem,&item);
1989 bResult = TRUE;
1992 LISTVIEW_SetItemFocus(hwnd, nItem);
1993 infoPtr->nSelectionMark = nItem;
1995 return bResult;
1998 /***
1999 * DESCRIPTION:
2000 * Selects items based on view coordinates.
2002 * PARAMETER(S):
2003 * [I] HWND : window handle
2004 * [I] RECT : selection rectangle
2006 * RETURN:
2007 * None
2009 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
2011 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2012 POINT ptItem;
2013 INT i;
2014 LVITEMW item;
2016 ZeroMemory(&item,sizeof(item));
2017 item.stateMask = LVIS_SELECTED;
2019 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
2021 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
2023 if (PtInRect(&rcSelRect, ptItem) != FALSE)
2024 item.state = LVIS_SELECTED;
2025 else
2026 item.state = 0;
2027 LISTVIEW_SetItemState(hwnd,i,&item);
2031 /***
2032 * DESCRIPTION:
2033 * Sets a single group selection.
2035 * PARAMETER(S):
2036 * [I] HWND : window handle
2037 * [I] INT : item index
2039 * RETURN:
2040 * None
2042 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
2044 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2045 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2046 LVITEMW item;
2048 ZeroMemory(&item,sizeof(item));
2049 item.stateMask = LVIS_SELECTED;
2051 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2053 INT i;
2054 INT nFirst, nLast;
2056 if (infoPtr->nSelectionMark == -1)
2058 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2060 else
2062 nFirst = min(infoPtr->nSelectionMark, nItem);
2063 nLast = max(infoPtr->nSelectionMark, nItem);
2066 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2068 if ((i < nFirst) || (i > nLast))
2069 item.state = 0;
2070 else
2071 item.state = LVIS_SELECTED;
2072 LISTVIEW_SetItemState(hwnd,i,&item);
2075 else
2077 RECT rcItem;
2078 RECT rcSelMark;
2079 RECT rcSel;
2080 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2081 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2082 rcSel.left = min(rcSelMark.left, rcItem.left);
2083 rcSel.top = min(rcSelMark.top, rcItem.top);
2084 rcSel.right = max(rcSelMark.right, rcItem.right);
2085 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2086 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2087 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2088 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2089 infoPtr->nSelectionMark,
2090 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2091 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2095 LISTVIEW_SetItemFocus(hwnd, nItem);
2098 /***
2099 * DESCRIPTION:
2100 * Manages the item focus.
2102 * PARAMETER(S):
2103 * [I] HWND : window handle
2104 * [I] INT : item index
2106 * RETURN:
2107 * TRUE : focused item changed
2108 * FALSE : focused item has NOT changed
2110 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2112 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2113 BOOL bResult = FALSE;
2114 LVITEMW lvItem;
2116 if (infoPtr->nFocusedItem != nItem)
2118 if (infoPtr->nFocusedItem >= 0)
2120 INT oldFocus = infoPtr->nFocusedItem;
2121 bResult = TRUE;
2122 infoPtr->nFocusedItem = -1;
2123 ZeroMemory(&lvItem, sizeof(lvItem));
2124 lvItem.stateMask = LVIS_FOCUSED;
2125 ListView_SetItemState(hwnd, oldFocus, &lvItem);
2129 lvItem.state = LVIS_FOCUSED;
2130 lvItem.stateMask = LVIS_FOCUSED;
2131 ListView_SetItemState(hwnd, nItem, &lvItem);
2133 infoPtr->nFocusedItem = nItem;
2134 ListView_EnsureVisible(hwnd, nItem, FALSE);
2137 return bResult;
2140 /***
2141 * DESCRIPTION:
2142 * Sets a single selection.
2144 * PARAMETER(S):
2145 * [I] HWND : window handle
2146 * [I] INT : item index
2148 * RETURN:
2149 * None
2151 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2153 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2154 LVITEMW lvItem;
2156 ZeroMemory(&lvItem, sizeof(lvItem));
2157 lvItem.stateMask = LVIS_FOCUSED;
2158 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2160 LISTVIEW_RemoveAllSelections(hwnd);
2162 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2163 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2164 ListView_SetItemState(hwnd, nItem, &lvItem);
2166 infoPtr->nFocusedItem = nItem;
2167 infoPtr->nSelectionMark = nItem;
2170 /***
2171 * DESCRIPTION:
2172 * Set selection(s) with keyboard.
2174 * PARAMETER(S):
2175 * [I] HWND : window handle
2176 * [I] INT : item index
2178 * RETURN:
2179 * SUCCESS : TRUE (needs to be repainted)
2180 * FAILURE : FALSE (nothing has changed)
2182 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2184 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2185 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2186 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2187 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2188 BOOL bResult = FALSE;
2190 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2192 if (lStyle & LVS_SINGLESEL)
2194 bResult = TRUE;
2195 LISTVIEW_SetSelection(hwnd, nItem);
2196 ListView_EnsureVisible(hwnd, nItem, FALSE);
2198 else
2200 if (wShift)
2202 bResult = TRUE;
2203 LISTVIEW_SetGroupSelection(hwnd, nItem);
2205 else if (wCtrl)
2207 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2209 else
2211 bResult = TRUE;
2212 LISTVIEW_SetSelection(hwnd, nItem);
2213 ListView_EnsureVisible(hwnd, nItem, FALSE);
2218 return bResult;
2221 /***
2222 * DESCRIPTION:
2223 * Called when the mouse is being actively tracked and has hovered for a specified
2224 * amount of time
2226 * PARAMETER(S):
2227 * [I] HWND : window handle
2228 * [I] wParam : key indicator
2229 * [I] lParam : mouse position
2231 * RETURN:
2232 * 0 if the message was processed, non-zero if there was an error
2234 * INFO:
2235 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2236 * over the item for a certain period of time.
2239 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2241 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2242 POINT pt;
2244 pt.x = (INT)LOWORD(lParam);
2245 pt.y = (INT)HIWORD(lParam);
2247 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2248 /* select the item under the cursor */
2249 LISTVIEW_MouseSelection(hwnd, pt);
2252 return 0;
2255 /***
2256 * DESCRIPTION:
2257 * Called whenever WM_MOUSEMOVE is received.
2259 * PARAMETER(S):
2260 * [I] HWND : window handle
2261 * [I] wParam : key indicators
2262 * [I] lParam : cursor position
2264 * RETURN:
2265 * 0 if the message is processed, non-zero if there was an error
2267 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2269 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2270 TRACKMOUSEEVENT trackinfo;
2272 /* see if we are supposed to be tracking mouse hovering */
2273 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2274 /* fill in the trackinfo struct */
2275 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2276 trackinfo.dwFlags = TME_QUERY;
2277 trackinfo.hwndTrack = hwnd;
2278 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2280 /* see if we are already tracking this hwnd */
2281 _TrackMouseEvent(&trackinfo);
2283 if(!(trackinfo.dwFlags & TME_HOVER)) {
2284 trackinfo.dwFlags = TME_HOVER;
2286 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2287 _TrackMouseEvent(&trackinfo);
2291 return 0;
2294 /***
2295 * DESCRIPTION:
2296 * Selects an item based on coordinates.
2298 * PARAMETER(S):
2299 * [I] HWND : window handle
2300 * [I] POINT : mouse click ccordinates
2302 * RETURN:
2303 * SUCCESS : item index
2304 * FAILURE : -1
2306 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2308 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2309 RECT rcItem;
2310 INT i,topindex,bottomindex;
2311 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2312 UINT uView = lStyle & LVS_TYPEMASK;
2314 topindex = ListView_GetTopIndex(hwnd);
2315 if (uView == LVS_REPORT)
2317 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2318 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2320 else
2322 bottomindex = GETITEMCOUNT(infoPtr);
2325 for (i = topindex; i < bottomindex; i++)
2327 rcItem.left = LVIR_SELECTBOUNDS;
2328 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2330 if (PtInRect(&rcItem, pt) != FALSE)
2332 return i;
2337 return -1;
2340 /***
2341 * DESCRIPTION:
2342 * Removes a column.
2344 * PARAMETER(S):
2345 * [IO] HDPA : dynamic pointer array handle
2346 * [I] INT : column index (subitem index)
2348 * RETURN:
2349 * SUCCCESS : TRUE
2350 * FAILURE : FALSE
2352 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2354 BOOL bResult = TRUE;
2355 HDPA hdpaSubItems;
2356 INT i;
2358 for (i = 0; i < hdpaItems->nItemCount; i++)
2360 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2361 if (hdpaSubItems != NULL)
2363 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2365 bResult = FALSE;
2370 return bResult;
2373 /***
2374 * DESCRIPTION:
2375 * Removes a subitem at a given position.
2377 * PARAMETER(S):
2378 * [IO] HDPA : dynamic pointer array handle
2379 * [I] INT : subitem index
2381 * RETURN:
2382 * SUCCCESS : TRUE
2383 * FAILURE : FALSE
2385 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2387 LISTVIEW_SUBITEM *lpSubItem;
2388 INT i;
2390 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2392 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2393 if (lpSubItem != NULL)
2395 if (lpSubItem->iSubItem == nSubItem)
2397 /* free string */
2398 if (is_textW(lpSubItem->pszText))
2399 COMCTL32_Free(lpSubItem->pszText);
2401 /* free item */
2402 COMCTL32_Free(lpSubItem);
2404 /* free dpa memory */
2405 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2406 return FALSE;
2408 else if (lpSubItem->iSubItem > nSubItem)
2409 return TRUE;
2413 return TRUE;
2416 /***
2417 * DESCRIPTION:
2418 * Compares the item information.
2420 * PARAMETER(S):
2421 * [I] LISTVIEW_ITEM *: destination item
2422 * [I] LPLVITEM : source item
2423 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2425 * RETURN:
2426 * SUCCCESS : TRUE (EQUAL)
2427 * FAILURE : FALSE (NOT EQUAL)
2429 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2431 UINT uChanged = 0;
2433 if ((lpItem != NULL) && (lpLVItem != NULL))
2435 if (lpLVItem->mask & LVIF_STATE)
2437 if ((lpItem->state & lpLVItem->stateMask) !=
2438 (lpLVItem->state & lpLVItem->stateMask))
2439 uChanged |= LVIF_STATE;
2442 if (lpLVItem->mask & LVIF_IMAGE)
2444 if (lpItem->iImage != lpLVItem->iImage)
2445 uChanged |= LVIF_IMAGE;
2448 if (lpLVItem->mask & LVIF_PARAM)
2450 if (lpItem->lParam != lpLVItem->lParam)
2451 uChanged |= LVIF_PARAM;
2454 if (lpLVItem->mask & LVIF_INDENT)
2456 if (lpItem->iIndent != lpLVItem->iIndent)
2457 uChanged |= LVIF_INDENT;
2460 if (lpLVItem->mask & LVIF_TEXT)
2462 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2464 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2465 uChanged |= LVIF_TEXT;
2467 else
2469 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2471 uChanged |= LVIF_TEXT;
2473 else
2475 if (lpLVItem->pszText)
2477 if (lpItem->pszText)
2479 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2480 if (pszText && strcmpW(pszText, lpItem->pszText))
2481 uChanged |= LVIF_TEXT;
2482 textfreeT(pszText, isW);
2484 else
2486 uChanged |= LVIF_TEXT;
2489 else
2491 if (lpItem->pszText)
2492 uChanged |= LVIF_TEXT;
2498 return uChanged;
2501 /***
2502 * DESCRIPTION:
2503 * Initializes item attributes.
2505 * PARAMETER(S):
2506 * [I] HWND : window handle
2507 * [O] LISTVIEW_ITEM *: destination item
2508 * [I] LPLVITEM : source item
2509 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2511 * RETURN:
2512 * SUCCCESS : TRUE
2513 * FAILURE : FALSE
2515 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2516 LPLVITEMW lpLVItem, BOOL isW)
2518 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2519 BOOL bResult = FALSE;
2521 if ((lpItem != NULL) && (lpLVItem != NULL))
2523 bResult = TRUE;
2525 if (lpLVItem->mask & LVIF_STATE)
2527 lpItem->state &= ~lpLVItem->stateMask;
2528 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2531 if (lpLVItem->mask & LVIF_IMAGE)
2532 lpItem->iImage = lpLVItem->iImage;
2534 if (lpLVItem->mask & LVIF_PARAM)
2535 lpItem->lParam = lpLVItem->lParam;
2537 if (lpLVItem->mask & LVIF_INDENT)
2538 lpItem->iIndent = lpLVItem->iIndent;
2540 if (lpLVItem->mask & LVIF_TEXT)
2542 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2544 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2545 return FALSE;
2547 if (is_textW(lpItem->pszText))
2548 COMCTL32_Free(lpItem->pszText);
2550 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2552 else
2553 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2557 return bResult;
2560 /***
2561 * DESCRIPTION:
2562 * Initializes subitem attributes.
2564 * NOTE: The documentation specifies that the operation fails if the user
2565 * tries to set the indent of a subitem.
2567 * PARAMETER(S):
2568 * [I] HWND : window handle
2569 * [O] LISTVIEW_SUBITEM *: destination subitem
2570 * [I] LPLVITEM : source subitem
2571 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2573 * RETURN:
2574 * SUCCCESS : TRUE
2575 * FAILURE : FALSE
2577 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2578 LPLVITEMW lpLVItem, BOOL isW)
2580 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2581 BOOL bResult = FALSE;
2583 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2584 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2586 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2588 if (!(lpLVItem->mask & LVIF_INDENT))
2590 bResult = TRUE;
2592 lpSubItem->iSubItem = lpLVItem->iSubItem;
2594 if (lpLVItem->mask & LVIF_IMAGE)
2595 lpSubItem->iImage = lpLVItem->iImage;
2597 if (lpLVItem->mask & LVIF_TEXT)
2599 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2601 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2602 return FALSE;
2604 if (is_textW(lpSubItem->pszText))
2605 COMCTL32_Free(lpSubItem->pszText);
2607 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2609 else
2610 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2615 return bResult;
2618 /***
2619 * DESCRIPTION:
2620 * Adds a subitem at a given position (column index).
2622 * PARAMETER(S):
2623 * [I] HWND : window handle
2624 * [I] LPLVITEM : new subitem atttributes
2625 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2627 * RETURN:
2628 * SUCCESS : TRUE
2629 * FAILURE : FALSE
2631 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2633 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2634 LISTVIEW_SUBITEM *lpSubItem = NULL;
2635 BOOL bResult = FALSE;
2636 HDPA hdpaSubItems;
2637 INT nPosition, nItem;
2638 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2640 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2642 if (lStyle & LVS_OWNERDATA)
2643 return FALSE;
2645 if (lpLVItem != NULL)
2647 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2648 if (hdpaSubItems != NULL)
2650 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2651 if (lpSubItem != NULL)
2653 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2654 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2656 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2657 lpSubItem->iSubItem);
2658 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2659 if (nItem != -1) bResult = TRUE;
2665 /* cleanup if unsuccessful */
2666 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2668 return bResult;
2671 /***
2672 * DESCRIPTION:
2673 * Finds the dpa insert position (array index).
2675 * PARAMETER(S):
2676 * [I] HWND : window handle
2677 * [I] INT : subitem index
2679 * RETURN:
2680 * SUCCESS : TRUE
2681 * FAILURE : FALSE
2683 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2685 LISTVIEW_SUBITEM *lpSubItem;
2686 INT i;
2688 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2690 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2691 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2692 return i;
2695 return hdpaSubItems->nItemCount;
2698 /***
2699 * DESCRIPTION:
2700 * Retrieves a listview subitem at a given position (column index).
2702 * PARAMETER(S):
2703 * [I] HWND : window handle
2704 * [I] INT : subitem index
2706 * RETURN:
2707 * SUCCESS : TRUE
2708 * FAILURE : FALSE
2710 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2712 LISTVIEW_SUBITEM *lpSubItem;
2713 INT i;
2715 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2717 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2718 if (lpSubItem != NULL)
2720 if (lpSubItem->iSubItem == nSubItem)
2721 return lpSubItem;
2722 else if (lpSubItem->iSubItem > nSubItem)
2723 return NULL;
2727 return NULL;
2730 /***
2731 * DESCRIPTION:
2732 * Sets item attributes.
2734 * PARAMETER(S):
2735 * [I] HWND : window handle
2736 * [I] LPLVITEM : new item atttributes
2737 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2739 * RETURN:
2740 * SUCCESS : TRUE
2741 * FAILURE : FALSE
2743 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2745 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2746 BOOL bResult = FALSE;
2747 HDPA hdpaSubItems;
2748 LISTVIEW_ITEM *lpItem;
2749 NMLISTVIEW nmlv;
2750 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2751 UINT uChanged;
2752 UINT uView = lStyle & LVS_TYPEMASK;
2753 INT item_width;
2754 RECT rcItem;
2756 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2758 if (lStyle & LVS_OWNERDATA)
2760 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2762 LVITEMW itm;
2764 ZeroMemory(&itm, sizeof(itm));
2765 itm.mask = LVIF_STATE | LVIF_PARAM;
2766 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2767 itm.iItem = lpLVItem->iItem;
2768 itm.iSubItem = 0;
2769 ListView_GetItemW(hwnd, &itm);
2772 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2773 nmlv.uNewState = lpLVItem->state;
2774 nmlv.uOldState = itm.state;
2775 nmlv.uChanged = LVIF_STATE;
2776 nmlv.lParam = itm.lParam;
2777 nmlv.iItem = lpLVItem->iItem;
2779 if ((itm.state & lpLVItem->stateMask) !=
2780 (lpLVItem->state & lpLVItem->stateMask))
2783 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent
2784 * by LVS_OWERNDATA list controls
2786 if (lpLVItem->stateMask & LVIS_FOCUSED)
2788 if (lpLVItem->state & LVIS_FOCUSED)
2789 infoPtr->nFocusedItem = lpLVItem->iItem;
2790 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2791 infoPtr->nFocusedItem = -1;
2793 if (lpLVItem->stateMask & LVIS_SELECTED)
2795 if (lpLVItem->state & LVIS_SELECTED)
2797 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2798 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2800 else
2801 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2802 lpLVItem->iItem);
2805 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2807 rcItem.left = LVIR_BOUNDS;
2808 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2809 if (!infoPtr->bIsDrawing)
2810 InvalidateRect(hwnd, &rcItem, TRUE);
2812 return TRUE;
2814 return FALSE;
2817 if (lpLVItem != NULL)
2819 if (lpLVItem->iSubItem == 0)
2821 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2822 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2824 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2825 if (lpItem != NULL)
2827 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2828 nmlv.lParam = lpItem->lParam;
2829 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2830 if (uChanged != 0)
2832 if (uChanged & LVIF_STATE)
2834 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2835 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2837 if (nmlv.uNewState & LVIS_SELECTED)
2840 * This is redundant if called through SetSelection
2842 * however is required if the used directly calls SetItem
2843 * to set the selection.
2845 if (lStyle & LVS_SINGLESEL)
2846 LISTVIEW_RemoveAllSelections(hwnd);
2848 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2849 lpLVItem->iItem);
2851 else if (lpLVItem->stateMask & LVIS_SELECTED)
2853 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2854 lpLVItem->iItem);
2856 if (nmlv.uNewState & LVIS_FOCUSED)
2859 * This is a fun hoop to jump to try to catch if
2860 * the user is calling us directly to call focus or if
2861 * this function is being called as a result of a
2862 * SetItemFocus call.
2864 if (infoPtr->nFocusedItem >= 0)
2865 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2869 nmlv.uChanged = uChanged;
2870 nmlv.iItem = lpLVItem->iItem;
2871 nmlv.lParam = lpItem->lParam;
2872 /* send LVN_ITEMCHANGING notification */
2873 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2875 /* copy information */
2876 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2878 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2879 based on the width of the items text */
2880 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2882 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2884 if(item_width > infoPtr->nItemWidth)
2885 infoPtr->nItemWidth = item_width;
2888 /* send LVN_ITEMCHANGED notification */
2889 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2891 else
2893 bResult = TRUE;
2896 if (uChanged)
2898 rcItem.left = LVIR_BOUNDS;
2899 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2900 if (!infoPtr->bIsDrawing)
2901 InvalidateRect(hwnd, &rcItem, TRUE);
2908 return bResult;
2911 /***
2912 * DESCRIPTION:
2913 * Sets subitem attributes.
2915 * PARAMETER(S):
2916 * [I] HWND : window handle
2917 * [I] LPLVITEM : new subitem atttributes
2918 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2920 * RETURN:
2921 * SUCCESS : TRUE
2922 * FAILURE : FALSE
2924 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2926 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2927 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2928 BOOL bResult = FALSE;
2929 HDPA hdpaSubItems;
2930 LISTVIEW_SUBITEM *lpSubItem;
2931 RECT rcItem;
2933 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2935 if (lStyle & LVS_OWNERDATA)
2936 return FALSE;
2938 if (lpLVItem != NULL)
2940 if (lpLVItem->iSubItem > 0)
2942 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2943 if (hdpaSubItems != NULL)
2945 /* set subitem only if column is present */
2946 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2948 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2949 if (lpSubItem != NULL)
2950 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2951 else
2952 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2954 rcItem.left = LVIR_BOUNDS;
2955 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2956 InvalidateRect(hwnd, &rcItem, FALSE);
2962 return bResult;
2965 /***
2966 * DESCRIPTION:
2967 * Sets item attributes.
2969 * PARAMETER(S):
2970 * [I] HWND : window handle
2971 * [I] LPLVITEM : new item atttributes
2972 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2974 * RETURN:
2975 * SUCCESS : TRUE
2976 * FAILURE : FALSE
2978 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2980 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2982 if (!lpLVItem || lpLVItem->iItem < 0 ||
2983 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2984 return FALSE;
2985 if (lpLVItem->iSubItem == 0)
2986 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2987 else
2988 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2991 /***
2992 * DESCRIPTION:
2993 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2995 * PARAMETER(S):
2996 * [I] HWND : window handle
2998 * RETURN:
2999 * item index
3001 static INT LISTVIEW_GetTopIndex(HWND hwnd)
3003 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3004 UINT uView = lStyle & LVS_TYPEMASK;
3005 INT nItem = 0;
3006 SCROLLINFO scrollInfo;
3008 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3009 scrollInfo.cbSize = sizeof(SCROLLINFO);
3010 scrollInfo.fMask = SIF_POS;
3012 if (uView == LVS_LIST)
3014 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
3015 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
3017 else if (uView == LVS_REPORT)
3019 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
3020 nItem = scrollInfo.nPos;
3023 return nItem;
3026 /***
3027 * DESCRIPTION:
3028 * Draws a subitem.
3030 * PARAMETER(S):
3031 * [I] HWND : window handle
3032 * [I] HDC : device context handle
3033 * [I] INT : item index
3034 * [I] INT : subitem index
3035 * [I] RECT * : clipping rectangle
3037 * RETURN:
3038 * None
3040 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
3041 RECT rcItem, BOOL Selected)
3043 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3044 WCHAR szDispText[DISP_TEXT_SIZE];
3045 LVITEMW lvItem;
3046 LVCOLUMNW lvColumn;
3047 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3048 RECT rcTemp;
3049 INT textLeft;
3050 INT nLabelWidth = 0;
3052 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3053 nItem, nSubItem);
3055 /* get information needed for drawing the item */
3056 ZeroMemory(&lvItem, sizeof(lvItem));
3057 lvItem.mask = LVIF_TEXT;
3058 lvItem.iItem = nItem;
3059 lvItem.iSubItem = nSubItem;
3060 lvItem.cchTextMax = DISP_TEXT_SIZE;
3061 lvItem.pszText = szDispText;
3062 *lvItem.pszText = '\0';
3063 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3064 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3066 ZeroMemory(&lvColumn, sizeof(lvColumn));
3067 lvColumn.mask = LVCF_FMT;
3068 LISTVIEW_GetColumnT(hwnd, nSubItem, &lvColumn, TRUE);
3069 textLeft = rcItem.left;
3070 TRACE("lvColumn.fmt=%d\n", lvColumn.fmt);
3071 if (lvColumn.fmt != LVCFMT_LEFT)
3073 if ((nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE)))
3075 if (lvColumn.fmt == LVCFMT_RIGHT)
3076 textLeft = rcItem.right - nLabelWidth;
3077 else
3078 textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
3083 /* redraw the background of the item */
3084 rcTemp = rcItem;
3085 if(infoPtr->nColumnCount == (nSubItem + 1))
3086 rcTemp.right = infoPtr->rcList.right;
3087 else
3088 rcTemp.right += WIDTH_PADDING;
3090 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3092 /* set item colors */
3093 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3095 if (infoPtr->bFocus)
3097 SetBkColor(hdc, comctl32_color.clrHighlight);
3098 SetTextColor(hdc, comctl32_color.clrHighlightText);
3100 else
3102 SetBkColor(hdc, comctl32_color.clr3dFace);
3103 SetTextColor(hdc, comctl32_color.clrBtnText);
3106 else
3108 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3110 SetBkMode(hdc, TRANSPARENT);
3111 textoutOptions &= ~ETO_OPAQUE;
3113 else
3115 SetBkMode(hdc, OPAQUE);
3116 SetBkColor(hdc, infoPtr->clrTextBk);
3119 SetTextColor(hdc, infoPtr->clrText);
3122 TRACE("drawing text %s, l=%d, t=%d, rect=(%d,%d)-(%d,%d)\n",
3123 debugstr_w(lvItem.pszText), textLeft, rcItem.top,
3124 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3125 ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
3126 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3128 if (Selected)
3130 /* fill in the gap */
3131 RECT rec;
3132 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3134 CopyRect(&rec,&rcItem);
3135 rec.left = rec.right;
3136 rec.right = rec.left+REPORT_MARGINX;
3137 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3138 &rec, NULL, 0, NULL);
3140 CopyRect(&rec,&rcItem);
3141 rec.right = rec.left;
3142 rec.left = rec.left - REPORT_MARGINX;
3143 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3144 &rec, NULL, 0, NULL);
3149 /***
3150 * DESCRIPTION:
3151 * Draws an item.
3153 * PARAMETER(S):
3154 * [I] HWND : window handle
3155 * [I] HDC : device context handle
3156 * [I] INT : item index
3157 * [I] RECT * : clipping rectangle
3159 * RETURN:
3160 * None
3162 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3164 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3165 WCHAR szDispText[DISP_TEXT_SIZE];
3166 INT nLabelWidth;
3167 LVITEMW lvItem;
3168 INT nMixMode;
3169 DWORD dwBkColor;
3170 DWORD dwTextColor,dwTextX;
3171 BOOL bImage = FALSE;
3172 INT iBkMode = -1;
3173 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3174 RECT rcTemp;
3176 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3179 /* get information needed for drawing the item */
3180 ZeroMemory(&lvItem, sizeof(lvItem));
3181 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3182 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3183 lvItem.iItem = nItem;
3184 lvItem.iSubItem = 0;
3185 lvItem.cchTextMax = DISP_TEXT_SIZE;
3186 lvItem.pszText = szDispText;
3187 *lvItem.pszText = '\0';
3188 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3189 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3191 /* redraw the background of the item */
3192 rcTemp = rcItem;
3193 if(infoPtr->nColumnCount == (nItem + 1))
3194 rcTemp.right = infoPtr->rcList.right;
3195 else
3196 rcTemp.right+=WIDTH_PADDING;
3198 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3200 /* do indent */
3201 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3203 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3205 if (SuggestedFocus)
3206 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3209 /* state icons */
3210 if (infoPtr->himlState != NULL)
3212 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3213 if (uStateImage > 0)
3215 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3216 rcItem.top, ILD_NORMAL);
3219 rcItem.left += infoPtr->iconSize.cx;
3220 if (SuggestedFocus)
3221 SuggestedFocus->left += infoPtr->iconSize.cx;
3222 bImage = TRUE;
3225 /* small icons */
3226 if (infoPtr->himlSmall != NULL)
3228 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3229 (lvItem.iImage>=0))
3231 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3232 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3233 rcItem.top, ILD_SELECTED);
3235 else if (lvItem.iImage>=0)
3237 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3238 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3239 rcItem.top, ILD_NORMAL);
3242 rcItem.left += infoPtr->iconSize.cx;
3244 if (SuggestedFocus)
3245 SuggestedFocus->left += infoPtr->iconSize.cx;
3246 bImage = TRUE;
3249 /* Don't bother painting item being edited */
3250 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3251 return;
3253 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3255 /* set item colors */
3256 dwBkColor = SetBkColor(hdc, comctl32_color.clrHighlight);
3257 dwTextColor = SetTextColor(hdc, comctl32_color.clrHighlightText);
3258 /* set raster mode */
3259 nMixMode = SetROP2(hdc, R2_XORPEN);
3261 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3262 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3264 dwBkColor = SetBkColor(hdc, comctl32_color.clr3dFace);
3265 dwTextColor = SetTextColor(hdc, comctl32_color.clrBtnText);
3266 /* set raster mode */
3267 nMixMode = SetROP2(hdc, R2_COPYPEN);
3269 else
3271 /* set item colors */
3272 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3274 dwBkColor = GetBkColor(hdc);
3275 iBkMode = SetBkMode(hdc, TRANSPARENT);
3276 textoutOptions &= ~ETO_OPAQUE;
3278 else
3280 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3281 iBkMode = SetBkMode(hdc, OPAQUE);
3284 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3285 /* set raster mode */
3286 nMixMode = SetROP2(hdc, R2_COPYPEN);
3289 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3290 if (rcItem.left + nLabelWidth < rcItem.right)
3292 if (!FullSelect)
3293 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3294 if (bImage)
3295 rcItem.right += IMAGE_PADDING;
3298 /* draw label */
3299 dwTextX = rcItem.left + 1;
3300 if (bImage)
3301 dwTextX += IMAGE_PADDING;
3303 if (lvItem.pszText) {
3304 TRACE("drawing text hwnd=%x, rect=(%d,%d)-(%d,%d)\n",
3305 hwnd, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3306 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3307 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3310 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3312 /* fill in the gap */
3313 RECT rec;
3314 CopyRect(&rec,&rcItem);
3315 rec.left = rec.right;
3316 rec.right = rec.left+REPORT_MARGINX;
3317 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3318 &rec, NULL, 0, NULL);
3321 if (!FullSelect)
3322 CopyRect(SuggestedFocus,&rcItem);
3324 if (nMixMode != 0)
3326 SetROP2(hdc, R2_COPYPEN);
3327 SetBkColor(hdc, dwBkColor);
3328 SetTextColor(hdc, dwTextColor);
3329 if (iBkMode != -1)
3330 SetBkMode(hdc, iBkMode);
3334 /***
3335 * DESCRIPTION:
3336 * Draws an item when in large icon display mode.
3338 * PARAMETER(S):
3339 * [I] HWND : window handle
3340 * [I] HDC : device context handle
3341 * [I] INT : item index
3342 * [I] RECT : clipping rectangle
3343 * [O] RECT * : The text rectangle about which to draw the focus
3345 * RETURN:
3346 * None
3348 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3349 RECT *SuggestedFocus)
3351 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3352 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3353 LVITEMW lvItem;
3354 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3355 DT_EDITCONTROL ;
3356 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3357 RECT rcTemp;
3359 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3360 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3362 /* get information needed for drawing the item */
3363 ZeroMemory(&lvItem, sizeof(lvItem));
3364 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3365 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3366 lvItem.iItem = nItem;
3367 lvItem.iSubItem = 0;
3368 lvItem.cchTextMax = DISP_TEXT_SIZE;
3369 lvItem.pszText = szDispText;
3370 *lvItem.pszText = '\0';
3371 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3372 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3374 /* redraw the background of the item */
3375 rcTemp = rcItem;
3376 if(infoPtr->nColumnCount == (nItem + 1))
3377 rcTemp.right = infoPtr->rcList.right;
3378 else
3379 rcTemp.right+=WIDTH_PADDING;
3380 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3382 TRACE("background rect (%d,%d)-(%d,%d)\n",
3383 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3385 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3388 /* Figure out text colours etc. depending on state
3389 * At least the following states exist; there may be more.
3390 * Many items may be selected
3391 * At most one item may have the focus
3392 * The application may not actually be active currently
3393 * 1. The item is not selected in any way
3394 * 2. The cursor is flying over the icon or text and the text is being
3395 * expanded because it is not fully displayed currently.
3396 * 3. The item is selected and is focussed, i.e. the user has not clicked
3397 * in the blank area of the window, and the window (or application?)
3398 * still has the focus.
3399 * 4. As 3 except that a different window has the focus
3400 * 5. The item is the selected item of all the items, but the user has
3401 * clicked somewhere else on the window.
3402 * Only a few of these are handled currently. In particular 2 is not yet
3403 * handled since we do not support the functionality currently (or at least
3404 * we didn't when I wrote this)
3407 if (lvItem.state & LVIS_SELECTED)
3409 /* set item colors */
3410 SetBkColor(hdc, comctl32_color.clrHighlight);
3411 SetTextColor(hdc, comctl32_color.clrHighlightText);
3412 SetBkMode (hdc, OPAQUE);
3413 /* set raster mode */
3414 SetROP2(hdc, R2_XORPEN);
3415 /* When exactly is it in XOR? while being dragged? */
3417 else
3419 /* set item colors */
3420 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3422 SetBkMode(hdc, TRANSPARENT);
3424 else
3426 SetBkMode(hdc, OPAQUE);
3427 SetBkColor(hdc, infoPtr->clrTextBk);
3430 SetTextColor(hdc, infoPtr->clrText);
3431 /* set raster mode */
3432 SetROP2(hdc, R2_COPYPEN);
3435 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3436 * wrapping and long words split.
3437 * In cases 1 and 4 only a portion of the text is displayed with word
3438 * wrapping and both word and end ellipsis. (I don't yet know about path
3439 * ellipsis)
3441 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3442 DT_NOCLIP
3444 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3446 /* draw the icon */
3447 if (infoPtr->himlNormal != NULL)
3449 if (lvItem.iImage >= 0)
3451 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3452 rcItem.top,
3453 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3457 /* Draw the text below the icon */
3459 /* Don't bother painting item being edited */
3460 if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
3461 !lstrlenW(lvItem.pszText))
3463 SetRectEmpty(SuggestedFocus);
3464 return;
3467 /* Since rcItem.left is left point of icon, compute left point of item box */
3468 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3469 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3470 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3471 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3472 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3473 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3474 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3475 infoPtr->rcList.left, infoPtr->rcList.top,
3476 infoPtr->rcList.right, infoPtr->rcList.bottom,
3477 infoPtr->rcView.left, infoPtr->rcView.top,
3478 infoPtr->rcView.right, infoPtr->rcView.bottom);
3480 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3481 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3484 /* draw label */
3486 /* I am sure of most of the uFormat values. However I am not sure about
3487 * whether we need or do not need the following:
3488 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3489 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3490 * We certainly do not need
3491 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3492 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3495 /* If the text is being drawn without clipping (i.e. the full text) then we
3496 * need to jump through a few hoops to ensure that it all gets displayed and
3497 * that the background is complete
3499 if (uFormat & DT_NOCLIP)
3501 RECT rcBack=rcItem;
3502 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3503 int dx, dy, old_wid, new_wid;
3504 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3505 /* Microsoft, in their great wisdom, have decided that the rectangle
3506 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3507 * not the location. So we have to do the centring ourselves (and take
3508 * responsibility for agreeing off-by-one consistency with them).
3510 old_wid = rcItem.right-rcItem.left;
3511 new_wid = rcBack.right - rcBack.left;
3512 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3513 dy = rcBack.top - rcItem.top;
3514 OffsetRect (&rcItem, dx, dy);
3515 FillRect(hdc, &rcItem, hBrush);
3516 DeleteObject(hBrush);
3518 /* else ? What if we are losing the focus? will we not get a complete
3519 * background?
3521 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3523 CopyRect(SuggestedFocus, &rcItem);
3526 /***
3527 * DESCRIPTION:
3528 * Draws listview items when in report display mode.
3530 * PARAMETER(S):
3531 * [I] HWND : window handle
3532 * [I] HDC : device context handle
3534 * RETURN:
3535 * None
3537 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3539 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3540 SCROLLINFO scrollInfo;
3541 INT nDrawPosY = infoPtr->rcList.top;
3542 INT nColumnCount;
3543 RECT rcItem, rcTemp;
3544 INT j;
3545 INT nItem;
3546 INT nLast;
3547 BOOL FullSelected;
3548 DWORD cditemmode = CDRF_DODEFAULT;
3549 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3550 INT scrollOffset;
3552 TRACE("\n");
3553 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3554 scrollInfo.cbSize = sizeof(SCROLLINFO);
3555 scrollInfo.fMask = SIF_POS;
3557 nItem = ListView_GetTopIndex(hwnd);
3559 /* add 1 for displaying a partial item at the bottom */
3560 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3561 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3563 /* send cache hint notification */
3564 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3566 NMLVCACHEHINT nmlv;
3568 nmlv.hdr.hwndFrom = hwnd;
3569 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3570 nmlv.hdr.code = LVN_ODCACHEHINT;
3571 nmlv.iFrom = nItem;
3572 nmlv.iTo = nLast;
3574 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3575 (LPARAM)&nmlv);
3578 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3579 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3580 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3582 /* clear the background of any part of the control that doesn't contain items */
3583 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3584 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3586 /* nothing to draw */
3587 if(GETITEMCOUNT(infoPtr) == 0)
3588 return;
3590 /* Get scroll bar info once before loop */
3591 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3592 scrollOffset = scrollInfo.nPos;
3594 for (; nItem < nLast; nItem++)
3596 RECT SuggestedFocusRect;
3598 /* Do Owner Draw */
3599 if (lStyle & LVS_OWNERDRAWFIXED)
3601 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3602 DRAWITEMSTRUCT dis;
3603 LVITEMW item;
3604 RECT br;
3606 TRACE("Owner Drawn\n");
3607 dis.CtlType = ODT_LISTVIEW;
3608 dis.CtlID = uID;
3609 dis.itemID = nItem;
3610 dis.itemAction = ODA_DRAWENTIRE;
3611 dis.itemState = 0;
3613 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3614 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3616 dis.hwndItem = hwnd;
3617 dis.hDC = hdc;
3619 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3621 dis.rcItem.left = -scrollOffset;
3622 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3623 dis.rcItem.top = nDrawPosY;
3624 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3626 ZeroMemory(&item,sizeof(item));
3627 item.iItem = nItem;
3628 item.mask = LVIF_PARAM;
3629 ListView_GetItemW(hwnd, &item);
3631 dis.itemData = item.lParam;
3633 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3635 nDrawPosY += infoPtr->nItemHeight;
3636 continue;
3640 if (FullSelected)
3642 RECT ir,br;
3644 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3645 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3647 ir.left += REPORT_MARGINX;
3648 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3649 ir.top = nDrawPosY;
3650 ir.bottom = ir.top + infoPtr->nItemHeight;
3652 CopyRect(&SuggestedFocusRect,&ir);
3655 for (j = 0; j < nColumnCount; j++)
3657 if (cdmode & CDRF_NOTIFYITEMDRAW)
3658 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3659 CDDS_ITEMPREPAINT);
3660 if (cditemmode & CDRF_SKIPDEFAULT)
3661 continue;
3663 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3665 rcItem.left += REPORT_MARGINX;
3666 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3667 rcItem.top = nDrawPosY;
3668 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3670 /* Offset the Scroll Bar Pos */
3671 rcItem.left -= scrollOffset;
3672 rcItem.right -= scrollOffset;
3674 if (j == 0)
3676 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3677 &SuggestedFocusRect);
3679 else
3681 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3684 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3685 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3686 CDDS_ITEMPOSTPAINT);
3689 * Draw Focus Rect
3691 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3693 BOOL rop=FALSE;
3694 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3695 rop = SetROP2(hdc, R2_XORPEN);
3697 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3698 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3700 if (rop)
3701 SetROP2(hdc, R2_COPYPEN);
3703 nDrawPosY += infoPtr->nItemHeight;
3707 /***
3708 * DESCRIPTION:
3709 * Retrieves the number of items that can fit vertically in the client area.
3711 * PARAMETER(S):
3712 * [I] HWND : window handle
3714 * RETURN:
3715 * Number of items per row.
3717 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3719 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3720 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3721 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3722 INT nCountPerRow = 1;
3724 if (nListWidth > 0)
3726 if (uView != LVS_REPORT)
3728 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3729 if (nCountPerRow == 0) nCountPerRow = 1;
3733 return nCountPerRow;
3736 /***
3737 * DESCRIPTION:
3738 * Retrieves the number of items that can fit horizontally in the client
3739 * area.
3741 * PARAMETER(S):
3742 * [I] HWND : window handle
3744 * RETURN:
3745 * Number of items per column.
3747 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3749 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3750 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3751 INT nCountPerColumn = 1;
3753 if (nListHeight > 0)
3755 TRACE("rcList=(%d,%d)-(%d,%d), nItemHeight=%d, CYHSCROLL=%d\n",
3756 infoPtr->rcList.left,infoPtr->rcList.top,
3757 infoPtr->rcList.right,infoPtr->rcList.bottom,
3758 infoPtr->nItemHeight,
3759 GetSystemMetrics(SM_CYHSCROLL));
3760 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3761 if (nCountPerColumn == 0) nCountPerColumn = 1;
3764 return nCountPerColumn;
3767 /***
3768 * DESCRIPTION:
3769 * Retrieves the number of columns needed to display all the items when in
3770 * list display mode.
3772 * PARAMETER(S):
3773 * [I] HWND : window handle
3775 * RETURN:
3776 * Number of columns.
3778 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3780 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3781 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3782 INT nColumnCount = 0;
3784 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3786 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3787 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3790 return nColumnCount;
3794 /***
3795 * DESCRIPTION:
3796 * Draws listview items when in list display mode.
3798 * PARAMETER(S):
3799 * [I] HWND : window handle
3800 * [I] HDC : device context handle
3802 * RETURN:
3803 * None
3805 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3807 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3808 RECT rcItem, FocusRect, rcTemp;
3809 INT i, j;
3810 INT nItem;
3811 INT nColumnCount;
3812 INT nCountPerColumn;
3813 INT nItemWidth = infoPtr->nItemWidth;
3814 INT nItemHeight = infoPtr->nItemHeight;
3815 DWORD cditemmode = CDRF_DODEFAULT;
3817 /* get number of fully visible columns */
3818 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3819 infoPtr->nColumnCount = nColumnCount;
3820 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3821 nItem = ListView_GetTopIndex(hwnd);
3822 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3823 nColumnCount, nCountPerColumn, nItem);
3825 /* paint the background of the control that doesn't contain any items */
3826 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3827 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3829 /* nothing to draw, return here */
3830 if(GETITEMCOUNT(infoPtr) == 0)
3831 return;
3833 for (i = 0; i < nColumnCount; i++)
3835 for (j = 0; j < nCountPerColumn; j++, nItem++)
3837 if (nItem >= GETITEMCOUNT(infoPtr))
3838 return;
3840 if (cdmode & CDRF_NOTIFYITEMDRAW)
3841 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3842 CDDS_ITEMPREPAINT);
3843 if (cditemmode & CDRF_SKIPDEFAULT)
3844 continue;
3846 rcItem.top = j * nItemHeight;
3847 rcItem.left = i * nItemWidth;
3848 rcItem.bottom = rcItem.top + nItemHeight;
3849 rcItem.right = rcItem.left + nItemWidth;
3850 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3852 * Draw Focus Rect
3854 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3855 Rectangle(hdc, FocusRect.left, FocusRect.top,
3856 FocusRect.right,FocusRect.bottom);
3858 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3859 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3860 CDDS_ITEMPOSTPAINT);
3866 /***
3867 * DESCRIPTION:
3868 * Draws listview items when in icon or small icon display mode.
3870 * PARAMETER(S):
3871 * [I] HWND : window handle
3872 * [I] HDC : device context handle
3874 * RETURN:
3875 * None
3877 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3879 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3880 POINT ptPosition;
3881 POINT ptOrigin;
3882 RECT rcItem, SuggestedFocus, rcTemp;
3883 INT i;
3884 DWORD cditemmode = CDRF_DODEFAULT;
3886 TRACE("\n");
3887 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3888 /* DrawItem from erasing the incorrect background area */
3890 /* paint the background of the control that doesn't contain any items */
3891 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3892 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3894 /* nothing to draw, return here */
3895 if(GETITEMCOUNT(infoPtr) == 0)
3896 return;
3898 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3899 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3901 if (cdmode & CDRF_NOTIFYITEMDRAW)
3902 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3903 CDDS_ITEMPREPAINT);
3904 if (cditemmode & CDRF_SKIPDEFAULT)
3905 continue;
3907 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3908 ptPosition.x += ptOrigin.x;
3909 ptPosition.y += ptOrigin.y;
3911 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3913 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3915 if (ptPosition.y < infoPtr->rcList.bottom)
3917 if (ptPosition.x < infoPtr->rcList.right)
3919 rcItem.top = ptPosition.y;
3920 rcItem.left = ptPosition.x;
3921 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3922 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3923 if (bSmall)
3924 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3925 else
3926 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3928 * Draw Focus Rect
3930 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3931 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3932 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3933 SuggestedFocus.right,SuggestedFocus.bottom);
3938 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3939 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3940 CDDS_ITEMPOSTPAINT);
3944 /***
3945 * DESCRIPTION:
3946 * Draws listview items.
3948 * PARAMETER(S):
3949 * [I] HWND : window handle
3950 * [I] HDC : device context handle
3952 * RETURN:
3953 * NoneX
3955 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3957 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3958 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3959 HFONT hOldFont;
3960 HPEN hPen, hOldPen;
3961 DWORD cdmode;
3962 RECT rect;
3964 infoPtr->bIsDrawing = TRUE;
3965 LISTVIEW_DumpListview (infoPtr, __LINE__);
3967 GetClientRect(hwnd, &rect);
3968 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3970 if (cdmode == CDRF_SKIPDEFAULT) return;
3972 /* select font */
3973 hOldFont = SelectObject(hdc, infoPtr->hFont);
3975 /* select the dotted pen (for drawing the focus box) */
3976 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3977 hOldPen = SelectObject(hdc, hPen);
3979 /* select transparent brush (for drawing the focus box) */
3980 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3982 TRACE("\n");
3983 if (uView == LVS_LIST)
3984 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3985 else if (uView == LVS_REPORT)
3986 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3987 else if (uView == LVS_SMALLICON)
3988 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3989 else if (uView == LVS_ICON)
3990 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3992 /* unselect objects */
3993 SelectObject(hdc, hOldFont);
3994 SelectObject(hdc, hOldPen);
3996 /* delete pen */
3997 DeleteObject(hPen);
3999 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4000 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
4002 infoPtr->bIsDrawing = FALSE;
4006 /***
4007 * DESCRIPTION:
4008 * Calculates the approximate width and height of a given number of items.
4010 * PARAMETER(S):
4011 * [I] HWND : window handle
4012 * [I] INT : number of items
4013 * [I] INT : width
4014 * [I] INT : height
4016 * RETURN:
4017 * Returns a DWORD. The width in the low word and the height in high word.
4019 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
4020 WORD wWidth, WORD wHeight)
4022 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4023 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4024 INT nItemCountPerColumn = 1;
4025 INT nColumnCount = 0;
4026 DWORD dwViewRect = 0;
4028 if (nItemCount == -1)
4029 nItemCount = GETITEMCOUNT(infoPtr);
4031 if (uView == LVS_LIST)
4033 if (wHeight == 0xFFFF)
4035 /* use current height */
4036 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4039 if (wHeight < infoPtr->nItemHeight)
4040 wHeight = infoPtr->nItemHeight;
4042 if (nItemCount > 0)
4044 if (infoPtr->nItemHeight > 0)
4046 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4047 if (nItemCountPerColumn == 0)
4048 nItemCountPerColumn = 1;
4050 if (nItemCount % nItemCountPerColumn != 0)
4051 nColumnCount = nItemCount / nItemCountPerColumn;
4052 else
4053 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4057 /* Microsoft padding magic */
4058 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4059 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4061 dwViewRect = MAKELONG(wWidth, wHeight);
4063 else if (uView == LVS_REPORT)
4064 FIXME("uView == LVS_REPORT: not implemented\n");
4065 else if (uView == LVS_SMALLICON)
4066 FIXME("uView == LVS_SMALLICON: not implemented\n");
4067 else if (uView == LVS_ICON)
4068 FIXME("uView == LVS_ICON: not implemented\n");
4070 return dwViewRect;
4073 /***
4074 * DESCRIPTION:
4075 * Arranges listview items in icon display mode.
4077 * PARAMETER(S):
4078 * [I] HWND : window handle
4079 * [I] INT : alignment code
4081 * RETURN:
4082 * SUCCESS : TRUE
4083 * FAILURE : FALSE
4085 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
4087 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4088 BOOL bResult = FALSE;
4090 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4092 switch (nAlignCode)
4094 case LVA_ALIGNLEFT:
4095 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4096 break;
4097 case LVA_ALIGNTOP:
4098 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4099 break;
4100 case LVA_DEFAULT:
4101 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4102 break;
4103 case LVA_SNAPTOGRID:
4104 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4105 break;
4109 return bResult;
4112 /* << LISTVIEW_CreateDragImage >> */
4115 /***
4116 * DESCRIPTION:
4117 * Removes all listview items and subitems.
4119 * PARAMETER(S):
4120 * [I] HWND : window handle
4122 * RETURN:
4123 * SUCCESS : TRUE
4124 * FAILURE : FALSE
4126 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4128 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4129 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4130 UINT uView = lStyle & LVS_TYPEMASK;
4131 LISTVIEW_ITEM *lpItem;
4132 LISTVIEW_SUBITEM *lpSubItem;
4133 NMLISTVIEW nmlv;
4134 BOOL bSuppress;
4135 BOOL bResult = FALSE;
4136 HDPA hdpaSubItems;
4138 TRACE("(hwnd=%x,)\n", hwnd);
4140 LISTVIEW_RemoveAllSelections(hwnd);
4141 infoPtr->nSelectionMark=-1;
4142 infoPtr->nFocusedItem=-1;
4143 /* But we are supposed to leave nHotItem as is! */
4145 if (lStyle & LVS_OWNERDATA)
4147 infoPtr->hdpaItems->nItemCount = 0;
4148 InvalidateRect(hwnd, NULL, TRUE);
4149 return TRUE;
4152 if (GETITEMCOUNT(infoPtr) > 0)
4154 INT i, j;
4156 /* send LVN_DELETEALLITEMS notification */
4157 /* verify if subsequent LVN_DELETEITEM notifications should be
4158 suppressed */
4159 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4160 nmlv.iItem = -1;
4161 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4163 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4165 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4166 if (hdpaSubItems != NULL)
4168 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4170 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4171 if (lpSubItem != NULL)
4173 /* free subitem string */
4174 if (is_textW(lpSubItem->pszText))
4175 COMCTL32_Free(lpSubItem->pszText);
4177 /* free subitem */
4178 COMCTL32_Free(lpSubItem);
4182 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4183 if (lpItem != NULL)
4185 if (!bSuppress)
4187 /* send LVN_DELETEITEM notification */
4188 nmlv.iItem = i;
4189 nmlv.lParam = lpItem->lParam;
4190 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4193 /* free item string */
4194 if (is_textW(lpItem->pszText))
4195 COMCTL32_Free(lpItem->pszText);
4197 /* free item */
4198 COMCTL32_Free(lpItem);
4201 DPA_Destroy(hdpaSubItems);
4205 /* reinitialize listview memory */
4206 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4208 /* align items (set position of each item) */
4209 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4211 if (lStyle & LVS_ALIGNLEFT)
4213 LISTVIEW_AlignLeft(hwnd);
4215 else
4217 LISTVIEW_AlignTop(hwnd);
4221 LISTVIEW_UpdateScroll(hwnd);
4223 /* invalidate client area (optimization needed) */
4224 InvalidateRect(hwnd, NULL, TRUE);
4227 return bResult;
4230 /***
4231 * DESCRIPTION:
4232 * Removes a column from the listview control.
4234 * PARAMETER(S):
4235 * [I] HWND : window handle
4236 * [I] INT : column index
4238 * RETURN:
4239 * SUCCESS : TRUE
4240 * FAILURE : FALSE
4242 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4244 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4245 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4246 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4247 BOOL bResult = FALSE;
4249 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4251 bResult = uOwnerData ? TRUE : LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4253 /* Need to reset the item width when deleting a column */
4254 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4256 /* reset scroll parameters */
4257 if (uView == LVS_REPORT)
4259 /* update scrollbar(s) */
4260 LISTVIEW_UpdateScroll(hwnd);
4262 /* refresh client area */
4263 InvalidateRect(hwnd, NULL, FALSE);
4267 return bResult;
4270 /***
4271 * DESCRIPTION:
4272 * Removes an item from the listview control.
4274 * PARAMETER(S):
4275 * [I] HWND : window handle
4276 * [I] INT : item index
4278 * RETURN:
4279 * SUCCESS : TRUE
4280 * FAILURE : FALSE
4282 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4284 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4285 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4286 UINT uView = lStyle & LVS_TYPEMASK;
4287 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4288 NMLISTVIEW nmlv;
4289 BOOL bResult = FALSE;
4290 HDPA hdpaSubItems;
4291 LISTVIEW_ITEM *lpItem;
4292 LISTVIEW_SUBITEM *lpSubItem;
4293 INT i;
4294 LVITEMW item;
4296 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4299 /* First, send LVN_DELETEITEM notification. */
4300 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4301 nmlv.hdr.hwndFrom = hwnd;
4302 nmlv.hdr.idFrom = lCtrlId;
4303 nmlv.hdr.code = LVN_DELETEITEM;
4304 nmlv.iItem = nItem;
4305 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4306 (LPARAM)&nmlv);
4309 /* remove it from the selection range */
4310 ZeroMemory(&item,sizeof(item));
4311 item.stateMask = LVIS_SELECTED;
4312 LISTVIEW_SetItemState(hwnd,nItem,&item);
4314 if (lStyle & LVS_OWNERDATA)
4316 infoPtr->hdpaItems->nItemCount --;
4317 InvalidateRect(hwnd, NULL, TRUE);
4318 return TRUE;
4321 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4323 /* initialize memory */
4324 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4326 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4327 if (hdpaSubItems != NULL)
4329 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4331 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4332 if (lpSubItem != NULL)
4334 /* free item string */
4335 if (is_textW(lpSubItem->pszText))
4336 COMCTL32_Free(lpSubItem->pszText);
4338 /* free item */
4339 COMCTL32_Free(lpSubItem);
4343 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4344 if (lpItem != NULL)
4346 /* free item string */
4347 if (is_textW(lpItem->pszText))
4348 COMCTL32_Free(lpItem->pszText);
4350 /* free item */
4351 COMCTL32_Free(lpItem);
4354 bResult = DPA_Destroy(hdpaSubItems);
4357 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4359 /* align items (set position of each item) */
4360 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4362 if (lStyle & LVS_ALIGNLEFT)
4363 LISTVIEW_AlignLeft(hwnd);
4364 else
4365 LISTVIEW_AlignTop(hwnd);
4368 LISTVIEW_UpdateScroll(hwnd);
4370 /* refresh client area */
4371 InvalidateRect(hwnd, NULL, TRUE);
4374 return bResult;
4378 /***
4379 * DESCRIPTION:
4380 * Return edit control handle of current edit label
4382 * PARAMETER(S):
4383 * [I] HWND : window handle
4385 * RETURN:
4386 * SUCCESS : HWND
4387 * FAILURE : 0
4389 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4391 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4392 return infoPtr->hwndEdit;
4396 /***
4397 * DESCRIPTION:
4398 * Callback implementation for editlabel control
4400 * PARAMETER(S):
4401 * [I] HWND : window handle
4402 * [I] LPSTR : modified text
4403 * [I] DWORD : item index
4404 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4406 * RETURN:
4407 * SUCCESS : TRUE
4408 * FAILURE : FALSE
4410 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4412 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4413 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4414 NMLVDISPINFOW dispInfo;
4415 LISTVIEW_ITEM *lpItem;
4416 HDPA hdpaSubItems;
4417 LISTVIEW_ITEM lvItemRef;
4418 LVITEMW item;
4419 BOOL bResult = TRUE;
4421 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4423 if (!(lStyle & LVS_OWNERDATA))
4425 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4426 return FALSE;
4428 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4429 return FALSE;
4431 else
4433 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4434 ZeroMemory(&item,sizeof(item));
4435 item.iItem = nItem;
4436 item.iSubItem = 0;
4437 item.mask = LVIF_PARAM | LVIF_STATE;
4438 ListView_GetItemW(hwnd, &item);
4439 lvItemRef.state = item.state;
4440 lvItemRef.iImage = item.iImage;
4441 lvItemRef.lParam = item.lParam;
4442 lpItem = &lvItemRef;
4445 ZeroMemory(&dispInfo, sizeof(dispInfo));
4446 dispInfo.item.mask = 0;
4447 dispInfo.item.iItem = nItem;
4448 dispInfo.item.state = lpItem->state;
4449 dispInfo.item.stateMask = 0;
4450 dispInfo.item.pszText = pszText;
4451 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4452 dispInfo.item.iImage = lpItem->iImage;
4453 dispInfo.item.lParam = lpItem->lParam;
4454 infoPtr->hwndEdit = 0;
4456 /* Do we need to update the Item Text */
4457 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4458 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4459 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4461 return bResult;
4464 /***
4465 * DESCRIPTION:
4466 * Callback implementation for editlabel control
4468 * PARAMETER(S):
4469 * [I] HWND : window handle
4470 * [I] LPSTR : modified text
4471 * [I] DWORD : item index
4473 * RETURN:
4474 * SUCCESS : TRUE
4475 * FAILURE : FALSE
4477 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4479 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4482 /***
4483 * DESCRIPTION:
4484 * Callback implementation for editlabel control
4486 * PARAMETER(S):
4487 * [I] HWND : window handle
4488 * [I] LPSTR : modified text
4489 * [I] DWORD : item index
4491 * RETURN:
4492 * SUCCESS : TRUE
4493 * FAILURE : FALSE
4495 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4497 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4500 /***
4501 * DESCRIPTION:
4502 * Begin in place editing of specified list view item
4504 * PARAMETER(S):
4505 * [I] HWND : window handle
4506 * [I] INT : item index
4507 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4509 * RETURN:
4510 * SUCCESS : TRUE
4511 * FAILURE : FALSE
4513 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4515 NMLVDISPINFOW dispInfo;
4516 RECT rect;
4517 LISTVIEW_ITEM *lpItem;
4518 HWND hedit;
4519 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4520 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4521 HDPA hdpaSubItems;
4522 WCHAR szDispText[DISP_TEXT_SIZE];
4523 LVITEMW lvItem;
4524 LISTVIEW_ITEM lvItemRef;
4525 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4527 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4528 return FALSE;
4530 /* Is the EditBox still there, if so remove it */
4531 if(infoPtr->hwndEdit != 0)
4532 SetFocus(hwnd);
4534 LISTVIEW_SetSelection(hwnd, nItem);
4535 LISTVIEW_SetItemFocus(hwnd, nItem);
4537 if (!(lStyle & LVS_OWNERDATA))
4539 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4540 return 0;
4542 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4543 return 0;
4545 else
4547 LVITEMW item;
4548 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4549 ZeroMemory(&item, sizeof(item));
4550 item.iItem = nItem;
4551 item.iSubItem = 0;
4552 item.mask = LVIF_PARAM | LVIF_STATE;
4553 ListView_GetItemW(hwnd, &item);
4554 lvItemRef.iImage = item.iImage;
4555 lvItemRef.state = item.state;
4556 lvItemRef.lParam = item.lParam;
4557 lpItem = &lvItemRef;
4560 /* get information needed for drawing the item */
4561 ZeroMemory(&lvItem, sizeof(lvItem));
4562 lvItem.mask = LVIF_TEXT;
4563 lvItem.iItem = nItem;
4564 lvItem.iSubItem = 0;
4565 lvItem.cchTextMax = DISP_TEXT_SIZE;
4566 lvItem.pszText = szDispText;
4567 *lvItem.pszText = '\0';
4568 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4570 ZeroMemory(&dispInfo, sizeof(dispInfo));
4571 dispInfo.item.mask = 0;
4572 dispInfo.item.iItem = nItem;
4573 dispInfo.item.state = lpItem->state;
4574 dispInfo.item.stateMask = 0;
4575 dispInfo.item.pszText = lvItem.pszText;
4576 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4577 dispInfo.item.iImage = lpItem->iImage;
4578 dispInfo.item.lParam = lpItem->lParam;
4580 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4581 return 0;
4583 rect.left = LVIR_LABEL;
4584 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4585 return 0;
4587 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4588 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4589 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4590 nItem, isW)))
4591 return 0;
4593 infoPtr->hwndEdit = hedit;
4594 SetFocus(hedit);
4595 SendMessageW(hedit, EM_SETSEL, 0, -1);
4597 return hedit;
4601 /***
4602 * DESCRIPTION:
4603 * Ensures the specified item is visible, scrolling into view if necessary.
4605 * PARAMETER(S):
4606 * [I] HWND : window handle
4607 * [I] INT : item index
4608 * [I] BOOL : partially or entirely visible
4610 * RETURN:
4611 * SUCCESS : TRUE
4612 * FAILURE : FALSE
4614 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4616 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4617 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4618 INT nScrollPosHeight = 0;
4619 INT nScrollPosWidth = 0;
4620 SCROLLINFO scrollInfo;
4621 RECT rcItem;
4622 BOOL bRedraw = FALSE;
4624 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4625 scrollInfo.cbSize = sizeof(SCROLLINFO);
4626 scrollInfo.fMask = SIF_POS;
4628 /* ALWAYS bPartial == FALSE, FOR NOW! */
4630 rcItem.left = LVIR_BOUNDS;
4631 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4633 if (rcItem.left < infoPtr->rcList.left)
4635 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4637 /* scroll left */
4638 bRedraw = TRUE;
4639 if (uView == LVS_LIST)
4641 nScrollPosWidth = infoPtr->nItemWidth;
4642 rcItem.left += infoPtr->rcList.left;
4644 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4646 nScrollPosWidth = 1;
4647 rcItem.left += infoPtr->rcList.left;
4650 /* When in LVS_REPORT view, the scroll position should
4651 not be updated. */
4652 if (nScrollPosWidth != 0)
4654 if (rcItem.left % nScrollPosWidth == 0)
4655 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4656 else
4657 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4659 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4663 else if (rcItem.right > infoPtr->rcList.right)
4665 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4667 /* scroll right */
4668 bRedraw = TRUE;
4669 if (uView == LVS_LIST)
4671 rcItem.right -= infoPtr->rcList.right;
4672 nScrollPosWidth = infoPtr->nItemWidth;
4674 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4676 rcItem.right -= infoPtr->rcList.right;
4677 nScrollPosWidth = 1;
4680 /* When in LVS_REPORT view, the scroll position should
4681 not be updated. */
4682 if (nScrollPosWidth != 0)
4684 if (rcItem.right % nScrollPosWidth == 0)
4685 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4686 else
4687 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4689 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4694 if (rcItem.top < infoPtr->rcList.top)
4696 /* scroll up */
4697 bRedraw = TRUE;
4698 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4700 if (uView == LVS_REPORT)
4702 rcItem.top -= infoPtr->rcList.top;
4703 nScrollPosHeight = infoPtr->nItemHeight;
4705 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4707 nScrollPosHeight = 1;
4708 rcItem.top += infoPtr->rcList.top;
4711 if (rcItem.top % nScrollPosHeight == 0)
4712 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4713 else
4714 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4716 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4719 else if (rcItem.bottom > infoPtr->rcList.bottom)
4721 /* scroll down */
4722 bRedraw = TRUE;
4723 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4725 if (uView == LVS_REPORT)
4727 rcItem.bottom -= infoPtr->rcList.bottom;
4728 nScrollPosHeight = infoPtr->nItemHeight;
4730 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4732 nScrollPosHeight = 1;
4733 rcItem.bottom -= infoPtr->rcList.bottom;
4736 if (rcItem.bottom % nScrollPosHeight == 0)
4737 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4738 else
4739 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4741 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4746 if(bRedraw)
4747 InvalidateRect(hwnd,NULL,TRUE);
4748 return TRUE;
4751 /***
4752 * DESCRIPTION:
4753 * Retrieves the nearest item, given a position and a direction.
4755 * PARAMETER(S):
4756 * [I] HWND : window handle
4757 * [I] POINT : start position
4758 * [I] UINT : direction
4760 * RETURN:
4761 * Item index if successdful, -1 otherwise.
4763 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4765 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4766 LV_INTHIT lvIntHit;
4767 INT nItem = -1;
4768 RECT rcView;
4770 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4771 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4772 ((vkDirection == VK_UP) ? "VK_UP" :
4773 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4775 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4777 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4778 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4779 lvIntHit.ht.pt.x += pt.x;
4780 lvIntHit.ht.pt.y += pt.y;
4782 if (vkDirection == VK_DOWN)
4783 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4784 else if (vkDirection == VK_UP)
4785 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4786 else if (vkDirection == VK_LEFT)
4787 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4788 else if (vkDirection == VK_RIGHT)
4789 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4791 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4792 return -1;
4793 else
4795 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4796 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4800 return nItem;
4803 /***
4804 * DESCRIPTION:
4805 * Searches for an item with specific characteristics.
4807 * PARAMETER(S):
4808 * [I] hwnd : window handle
4809 * [I] nStart : base item index
4810 * [I] lpFindInfo : item information to look for
4812 * RETURN:
4813 * SUCCESS : index of item
4814 * FAILURE : -1
4816 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4817 LPLVFINDINFOW lpFindInfo)
4819 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4820 POINT ptItem;
4821 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4822 LVITEMW lvItem;
4823 BOOL bWrap = FALSE;
4824 INT nItem = nStart;
4825 INT nLast = GETITEMCOUNT(infoPtr);
4827 if ((nItem >= -1) && (lpFindInfo != NULL))
4829 ZeroMemory(&lvItem, sizeof(lvItem));
4831 if (lpFindInfo->flags & LVFI_PARAM)
4833 lvItem.mask |= LVIF_PARAM;
4836 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4838 lvItem.mask |= LVIF_TEXT;
4839 lvItem.pszText = szDispText;
4840 lvItem.cchTextMax = DISP_TEXT_SIZE;
4843 if (lpFindInfo->flags & LVFI_WRAP)
4844 bWrap = TRUE;
4846 if (lpFindInfo->flags & LVFI_NEARESTXY)
4848 ptItem.x = lpFindInfo->pt.x;
4849 ptItem.y = lpFindInfo->pt.y;
4852 while (1)
4854 while (nItem < nLast)
4856 if (lpFindInfo->flags & LVFI_NEARESTXY)
4858 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4859 lpFindInfo->vkDirection);
4860 if (nItem != -1)
4862 /* get position of the new item index */
4863 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4864 return -1;
4866 else
4867 return -1;
4869 else
4871 nItem++;
4874 lvItem.iItem = nItem;
4875 lvItem.iSubItem = 0;
4876 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4878 if (lvItem.mask & LVIF_TEXT)
4880 if (lpFindInfo->flags & LVFI_PARTIAL)
4882 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4883 continue;
4885 else
4887 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4888 continue;
4892 if (lvItem.mask & LVIF_PARAM)
4894 if (lpFindInfo->lParam != lvItem.lParam)
4895 continue;
4898 return nItem;
4902 if (bWrap)
4904 nItem = -1;
4905 nLast = nStart + 1;
4906 bWrap = FALSE;
4908 else
4910 return -1;
4915 return -1;
4918 /***
4919 * DESCRIPTION:
4920 * Searches for an item with specific characteristics.
4922 * PARAMETER(S):
4923 * [I] hwnd : window handle
4924 * [I] nStart : base item index
4925 * [I] lpFindInfo : item information to look for
4927 * RETURN:
4928 * SUCCESS : index of item
4929 * FAILURE : -1
4931 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4932 LPLVFINDINFOA lpFindInfo)
4934 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4935 LVFINDINFOW fiw;
4936 LRESULT res;
4938 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4939 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4940 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4941 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4942 return res;
4945 /***
4946 * DESCRIPTION:
4947 * Retrieves the background color of the listview control.
4949 * PARAMETER(S):
4950 * [I] HWND : window handle
4952 * RETURN:
4953 * COLORREF associated with the background.
4955 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4957 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4959 return infoPtr->clrBk;
4962 /***
4963 * DESCRIPTION:
4964 * Retrieves the background image of the listview control.
4966 * PARAMETER(S):
4967 * [I] HWND : window handle
4968 * [O] LPLVMKBIMAGE : background image attributes
4970 * RETURN:
4971 * SUCCESS : TRUE
4972 * FAILURE : FALSE
4974 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4975 /* { */
4976 /* FIXME (listview, "empty stub!\n"); */
4977 /* return FALSE; */
4978 /* } */
4980 /***
4981 * DESCRIPTION:
4982 * Retrieves the callback mask.
4984 * PARAMETER(S):
4985 * [I] HWND : window handle
4987 * RETURN:
4988 * Value of mask
4990 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4992 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4994 return infoPtr->uCallbackMask;
4997 /***
4998 * DESCRIPTION:
4999 * Retrieves column attributes.
5001 * PARAMETER(S):
5002 * [I] HWND : window handle
5003 * [I] INT : column index
5004 * [IO] LPLVCOLUMNW : column information
5005 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5006 * otherwise it is in fact a LPLVCOLUMNA
5008 * RETURN:
5009 * SUCCESS : TRUE
5010 * FAILURE : FALSE
5012 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
5014 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5015 HDITEMW hdi;
5016 BOOL bResult = FALSE;
5018 if (lpColumn != NULL)
5021 /* initialize memory */
5022 ZeroMemory(&hdi, sizeof(hdi));
5024 if (lpColumn->mask & LVCF_FMT)
5025 hdi.mask |= HDI_FORMAT;
5027 if (lpColumn->mask & LVCF_WIDTH)
5028 hdi.mask |= HDI_WIDTH;
5030 if (lpColumn->mask & LVCF_TEXT)
5032 hdi.mask |= HDI_TEXT;
5033 hdi.cchTextMax = lpColumn->cchTextMax;
5034 hdi.pszText = lpColumn->pszText;
5037 if (lpColumn->mask & LVCF_IMAGE)
5038 hdi.mask |= HDI_IMAGE;
5040 if (lpColumn->mask & LVCF_ORDER)
5041 hdi.mask |= HDI_ORDER;
5043 if (isW)
5044 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
5045 else
5046 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
5048 if (bResult != FALSE)
5050 if (lpColumn->mask & LVCF_FMT)
5052 lpColumn->fmt = 0;
5054 if (hdi.fmt & HDF_LEFT)
5055 lpColumn->fmt |= LVCFMT_LEFT;
5056 else if (hdi.fmt & HDF_RIGHT)
5057 lpColumn->fmt |= LVCFMT_RIGHT;
5058 else if (hdi.fmt & HDF_CENTER)
5059 lpColumn->fmt |= LVCFMT_CENTER;
5061 if (hdi.fmt & HDF_IMAGE)
5062 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
5064 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
5065 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
5068 if (lpColumn->mask & LVCF_WIDTH)
5069 lpColumn->cx = hdi.cxy;
5071 if (lpColumn->mask & LVCF_IMAGE)
5072 lpColumn->iImage = hdi.iImage;
5074 if (lpColumn->mask & LVCF_ORDER)
5075 lpColumn->iOrder = hdi.iOrder;
5077 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
5078 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
5083 return bResult;
5087 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
5089 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5090 INT i;
5092 if (!lpiArray)
5093 return FALSE;
5095 /* little hack */
5096 for (i = 0; i < iCount; i++)
5097 lpiArray[i] = i;
5099 return TRUE;
5102 /***
5103 * DESCRIPTION:
5104 * Retrieves the column width.
5106 * PARAMETER(S):
5107 * [I] HWND : window handle
5108 * [I] int : column index
5110 * RETURN:
5111 * SUCCESS : column width
5112 * FAILURE : zero
5114 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5116 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5117 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5118 INT nColumnWidth = 0;
5119 HDITEMW hdi;
5121 if (uView == LVS_LIST)
5123 nColumnWidth = infoPtr->nItemWidth;
5125 else if (uView == LVS_REPORT)
5127 /* get column width from header */
5128 ZeroMemory(&hdi, sizeof(hdi));
5129 hdi.mask = HDI_WIDTH;
5130 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5131 nColumnWidth = hdi.cxy;
5134 return nColumnWidth;
5137 /***
5138 * DESCRIPTION:
5139 * In list or report display mode, retrieves the number of items that can fit
5140 * vertically in the visible area. In icon or small icon display mode,
5141 * retrieves the total number of visible items.
5143 * PARAMETER(S):
5144 * [I] HWND : window handle
5146 * RETURN:
5147 * Number of fully visible items.
5149 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5151 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5152 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5153 INT nItemCount = 0;
5155 if (uView == LVS_LIST)
5157 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5159 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5160 LISTVIEW_GetCountPerColumn(hwnd);
5163 else if (uView == LVS_REPORT)
5165 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5167 else
5169 nItemCount = GETITEMCOUNT(infoPtr);
5172 return nItemCount;
5175 /* LISTVIEW_GetEditControl */
5177 /***
5178 * DESCRIPTION:
5179 * Retrieves the extended listview style.
5181 * PARAMETERS:
5182 * [I] HWND : window handle
5184 * RETURN:
5185 * SUCCESS : previous style
5186 * FAILURE : 0
5188 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5190 LISTVIEW_INFO *infoPtr;
5192 /* make sure we can get the listview info */
5193 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5194 return (0);
5196 return (infoPtr->dwExStyle);
5199 /***
5200 * DESCRIPTION:
5201 * Retrieves the handle to the header control.
5203 * PARAMETER(S):
5204 * [I] HWND : window handle
5206 * RETURN:
5207 * Header handle.
5209 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5211 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5213 return infoPtr->hwndHeader;
5216 /* LISTVIEW_GetHotCursor */
5218 /***
5219 * DESCRIPTION:
5220 * Returns the time that the mouse cursor must hover over an item
5221 * before it is selected.
5223 * PARAMETER(S):
5224 * [I] HWND : window handle
5226 * RETURN:
5227 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5228 * hover time is set to the default hover time.
5230 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5232 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5234 return infoPtr->dwHoverTime;
5237 /***
5238 * DESCRIPTION:
5239 * Retrieves an image list handle.
5241 * PARAMETER(S):
5242 * [I] HWND : window handle
5243 * [I] INT : image list identifier
5245 * RETURN:
5246 * SUCCESS : image list handle
5247 * FAILURE : NULL
5249 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5251 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5252 HIMAGELIST himl = NULL;
5254 switch (nImageList)
5256 case LVSIL_NORMAL:
5257 himl = infoPtr->himlNormal;
5258 break;
5259 case LVSIL_SMALL:
5260 himl = infoPtr->himlSmall;
5261 break;
5262 case LVSIL_STATE:
5263 himl = infoPtr->himlState;
5264 break;
5267 return (LRESULT)himl;
5270 /* LISTVIEW_GetISearchString */
5272 /***
5273 * DESCRIPTION:
5274 * Retrieves item attributes.
5276 * PARAMETER(S):
5277 * [I] hwnd : window handle
5278 * [IO] lpLVItem : item info
5279 * [I] internal : if true then we will use tricks that avoid copies
5280 * but are not compatible with the regular interface
5281 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5282 * if FALSE, the lpLVItem is a LPLVITEMA.
5284 * RETURN:
5285 * SUCCESS : TRUE
5286 * FAILURE : FALSE
5288 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5290 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5291 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5292 NMLVDISPINFOW dispInfo;
5293 LISTVIEW_SUBITEM *lpSubItem;
5294 LISTVIEW_ITEM *lpItem;
5295 HDPA hdpaSubItems;
5296 void* null = NULL;
5297 INT* piImage = (INT*)&null;
5298 LPWSTR* ppszText= (LPWSTR*)&null;
5299 LPARAM* plParam = (LPARAM*)&null;
5301 if (internal && !isW)
5303 ERR("We can't have internal non-Unicode GetItem!\n");
5304 return FALSE;
5307 /* In the following:
5308 * lpLVItem describes the information requested by the user
5309 * lpItem/lpSubItem is what we have
5310 * dispInfo is a structure we use to request the missing
5311 * information from the application
5314 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5315 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5317 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5318 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5319 return FALSE;
5321 ZeroMemory(&dispInfo, sizeof(dispInfo));
5323 if (lStyle & LVS_OWNERDATA)
5325 if (lpLVItem->mask & ~LVIF_STATE)
5327 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5328 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5329 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5330 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5333 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5335 lpLVItem->state = 0;
5336 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5337 lpLVItem->state |= LVIS_FOCUSED;
5338 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5339 lpLVItem->state |= LVIS_SELECTED;
5342 return TRUE;
5345 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5346 if (hdpaSubItems == NULL) return FALSE;
5348 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5349 return FALSE;
5351 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5352 if (lpLVItem->iSubItem == 0)
5354 piImage=&lpItem->iImage;
5355 ppszText=&lpItem->pszText;
5356 plParam=&lpItem->lParam;
5357 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5359 dispInfo.item.mask |= LVIF_STATE;
5360 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5363 else
5365 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5366 if (lpSubItem != NULL)
5368 piImage=&lpSubItem->iImage;
5369 ppszText=&lpSubItem->pszText;
5373 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5375 dispInfo.item.mask |= LVIF_IMAGE;
5378 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5380 dispInfo.item.mask |= LVIF_TEXT;
5381 dispInfo.item.pszText = lpLVItem->pszText;
5382 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5383 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5384 *dispInfo.item.pszText = '\0';
5385 if (dispInfo.item.pszText && (*ppszText == NULL))
5386 *dispInfo.item.pszText = '\0';
5389 if (dispInfo.item.mask != 0)
5391 /* We don't have all the requested info, query the application */
5392 dispInfo.item.iItem = lpLVItem->iItem;
5393 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5394 dispInfo.item.lParam = lpItem->lParam;
5395 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5396 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5399 if (dispInfo.item.mask & LVIF_IMAGE)
5401 lpLVItem->iImage = dispInfo.item.iImage;
5402 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5403 *piImage = dispInfo.item.iImage;
5405 else if (lpLVItem->mask & LVIF_IMAGE)
5407 lpLVItem->iImage = *piImage;
5410 if (dispInfo.item.mask & LVIF_PARAM)
5412 lpLVItem->lParam = dispInfo.item.lParam;
5413 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5414 *plParam = dispInfo.item.lParam;
5416 else if (lpLVItem->mask & LVIF_PARAM)
5417 lpLVItem->lParam = lpItem->lParam;
5419 if (dispInfo.item.mask & LVIF_TEXT)
5421 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5422 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5424 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5425 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5426 if (lpLVItem->pszText != dispInfo.item.pszText)
5427 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5430 else if (lpLVItem->mask & LVIF_TEXT)
5432 if (internal) lpLVItem->pszText = *ppszText;
5433 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5436 if (lpLVItem->iSubItem == 0)
5438 if (dispInfo.item.mask & LVIF_STATE)
5440 lpLVItem->state = lpItem->state;
5441 lpLVItem->state &= ~dispInfo.item.stateMask;
5442 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5444 lpLVItem->state &= ~LVIS_SELECTED;
5445 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5446 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5447 lpLVItem->state |= LVIS_SELECTED;
5449 else if (lpLVItem->mask & LVIF_STATE)
5451 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5453 lpLVItem->state &= ~LVIS_SELECTED;
5454 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5455 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5456 lpLVItem->state |= LVIS_SELECTED;
5459 if (lpLVItem->mask & LVIF_PARAM)
5460 lpLVItem->lParam = lpItem->lParam;
5462 if (lpLVItem->mask & LVIF_INDENT)
5463 lpLVItem->iIndent = lpItem->iIndent;
5466 return TRUE;
5469 /* LISTVIEW_GetHotCursor */
5471 /***
5472 * DESCRIPTION:
5473 * Retrieves the index of the hot item.
5475 * PARAMETERS:
5476 * [I] HWND : window handle
5478 * RETURN:
5479 * SUCCESS : hot item index
5480 * FAILURE : -1 (no hot item)
5482 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5484 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5486 return infoPtr->nHotItem;
5489 /* LISTVIEW_GetHoverTime */
5491 /***
5492 * DESCRIPTION:
5493 * Retrieves the number of items in the listview control.
5495 * PARAMETER(S):
5496 * [I] HWND : window handle
5498 * RETURN:
5499 * Number of items.
5501 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5503 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5505 return GETITEMCOUNT(infoPtr);
5508 /***
5509 * DESCRIPTION:
5510 * Retrieves the rectangle enclosing the item icon and text.
5512 * PARAMETER(S):
5513 * [I] HWND : window handle
5514 * [I] INT : item index
5515 * [O] LPRECT : coordinate information
5517 * RETURN:
5518 * SUCCESS : TRUE
5519 * FAILURE : FALSE
5521 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5523 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5524 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5525 UINT uView = lStyle & LVS_TYPEMASK;
5526 BOOL bResult = FALSE;
5527 HDPA hdpaSubItems;
5528 LISTVIEW_ITEM *lpItem;
5529 INT nCountPerColumn;
5530 INT nRow;
5532 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5534 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5535 (lpRect != NULL))
5537 if (uView == LVS_LIST)
5539 bResult = TRUE;
5540 nItem = nItem - ListView_GetTopIndex(hwnd);
5541 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5542 if (nItem < 0)
5544 nRow = nItem % nCountPerColumn;
5545 if (nRow == 0)
5547 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5548 lpRect->top = 0;
5550 else
5552 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5553 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5556 else
5558 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5559 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5562 else if (uView == LVS_REPORT)
5564 bResult = TRUE;
5565 lpRect->left = REPORT_MARGINX;
5566 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5567 infoPtr->nItemHeight) + infoPtr->rcList.top;
5569 if (!(lStyle & LVS_NOSCROLL))
5571 SCROLLINFO scrollInfo;
5572 /* Adjust position by scrollbar offset */
5573 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5574 scrollInfo.cbSize = sizeof(SCROLLINFO);
5575 scrollInfo.fMask = SIF_POS;
5576 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5577 lpRect->left -= scrollInfo.nPos;
5580 else /* either LVS_ICON or LVS_SMALLICON */
5582 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5584 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5586 bResult = TRUE;
5587 lpRect->left = lpItem->ptPosition.x;
5588 lpRect->top = lpItem->ptPosition.y;
5593 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5594 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5595 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5596 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5597 return bResult;
5600 /***
5601 * DESCRIPTION:
5602 * Retrieves the position (upper-left) of the listview control item.
5603 * Note that for LVS_ICON style, the upper-left is that of the icon
5604 * and not the bounding box.
5606 * PARAMETER(S):
5607 * [I] HWND : window handle
5608 * [I] INT : item index
5609 * [O] LPPOINT : coordinate information
5611 * RETURN:
5612 * SUCCESS : TRUE
5613 * FAILURE : FALSE
5615 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5617 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5618 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5619 BOOL bResult = FALSE;
5620 RECT rcBounding;
5622 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5624 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5625 (lpptPosition != NULL))
5627 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5628 lpptPosition->x = rcBounding.left;
5629 lpptPosition->y = rcBounding.top;
5630 if (uView == LVS_ICON)
5632 lpptPosition->y += ICON_TOP_PADDING;
5633 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5635 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5636 lpptPosition->x, lpptPosition->y);
5638 return bResult;
5641 /***
5642 * Update the bounding rectangle around the text under a large icon.
5643 * This depends on whether it has the focus or not.
5644 * On entry the rectangle's top, left and right should be set.
5645 * On return the bottom will also be set and the width may have been
5646 * modified.
5648 * This appears to be weird, even in the Microsoft implementation.
5651 static void ListView_UpdateLargeItemLabelRect (
5652 HWND hwnd, /* The window of the listview */
5653 const LISTVIEW_INFO *infoPtr, /* The listview itself */
5654 int nItem, /* The item for which we are calculating this */
5655 RECT *rect) /* The rectangle to be updated */
5657 HDC hdc = GetDC (hwnd);
5658 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5660 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5662 /* We (aim to) display the full text. In Windows 95 it appears to
5663 * calculate the size assuming the specified font and then it draws
5664 * the text in that region with the specified font except scaled to
5665 * 10 point (or the height of the system font or ...). Thus if the
5666 * window has 24 point Helvetica the highlit rectangle will be
5667 * taller than the text and if it is 7 point Helvetica then the text
5668 * will be clipped.
5669 * For now we will simply say that it is the correct size to display
5670 * the text in the specified font.
5672 LVITEMW lvItem;
5673 lvItem.mask = LVIF_TEXT;
5674 lvItem.iItem = nItem;
5675 lvItem.iSubItem = 0;
5676 /* We will specify INTERNAL and so will receive back a const
5677 * pointer to the text, rather than specifying a buffer to which
5678 * to copy it.
5680 LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5681 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5682 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5683 DT_WORDBREAK | DT_NOPREFIX);
5684 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5686 else
5688 /* As far as I can see the text region seems to be trying to be
5689 * "tall enough for two lines of text". Once again (comctl32.dll ver
5690 * 5.81?) it measures this on the basis of the selected font and then
5691 * draws it with the same font except in 10 point size. This can lead
5692 * to more or less than the two rows appearing.
5693 * Question; are we supposed to be including DT_EXTERNALLEADING?
5694 * Question; should the width be shrunk to the space required to
5695 * display the two lines?
5697 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5700 SelectObject (hdc, hOldFont);
5701 ReleaseDC (hwnd, hdc);
5704 /***
5705 * DESCRIPTION:
5706 * Retrieves the bounding rectangle for a listview control item.
5708 * PARAMETER(S):
5709 * [I] HWND : window handle
5710 * [I] INT : item index
5711 * [IO] LPRECT : bounding rectangle coordinates
5712 * lprc->left specifies the portion of the item for which the bounding
5713 * rectangle will be retrieved.
5715 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5716 * including the icon and label.
5717 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5718 * LVIR_LABEL Returns the bounding rectangle of the item text.
5719 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5720 * rectangles, but excludes columns in report view.
5722 * RETURN:
5723 * SUCCESS : TRUE
5724 * FAILURE : FALSE
5726 * NOTES
5727 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5728 * upon whether the window has the focus currently and on whether the item
5729 * is the one with the focus. Ensure that the control's record of which
5730 * item has the focus agrees with the items' records.
5732 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5734 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5735 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5736 BOOL bResult = FALSE;
5737 POINT ptOrigin;
5738 POINT ptItem;
5739 INT nLeftPos;
5740 INT nLabelWidth;
5741 INT nIndent;
5742 LVITEMW lvItem;
5743 RECT rcInternal;
5745 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5747 if (uView & LVS_REPORT)
5749 ZeroMemory(&lvItem, sizeof(lvItem));
5750 lvItem.mask = LVIF_INDENT;
5751 lvItem.iItem = nItem;
5752 lvItem.iSubItem = 0;
5753 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5755 /* do indent */
5756 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5757 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5758 else
5759 nIndent = 0;
5761 else
5762 nIndent = 0;
5764 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5766 switch(lprc->left)
5768 case LVIR_ICON:
5769 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5770 if (uView == LVS_ICON)
5772 if (infoPtr->himlNormal != NULL)
5774 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5776 bResult = TRUE;
5777 lprc->left = ptItem.x + ptOrigin.x;
5778 lprc->top = ptItem.y + ptOrigin.y;
5779 lprc->right = lprc->left + infoPtr->iconSize.cx;
5780 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5781 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5785 else if (uView == LVS_SMALLICON)
5787 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5789 bResult = TRUE;
5790 lprc->left = ptItem.x + ptOrigin.x;
5791 lprc->top = ptItem.y + ptOrigin.y;
5792 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5794 if (infoPtr->himlState != NULL)
5795 lprc->left += infoPtr->iconSize.cx;
5797 if (infoPtr->himlSmall != NULL)
5798 lprc->right = lprc->left + infoPtr->iconSize.cx;
5799 else
5800 lprc->right = lprc->left;
5803 else
5805 bResult = TRUE;
5806 lprc->left = ptItem.x;
5807 if (uView & LVS_REPORT)
5808 lprc->left += nIndent;
5809 lprc->top = ptItem.y;
5810 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5812 if (infoPtr->himlState != NULL)
5813 lprc->left += infoPtr->iconSize.cx;
5815 if (infoPtr->himlSmall != NULL)
5816 lprc->right = lprc->left + infoPtr->iconSize.cx;
5817 else
5818 lprc->right = lprc->left;
5820 break;
5822 case LVIR_LABEL:
5823 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5824 if (uView == LVS_ICON)
5826 if (infoPtr->himlNormal != NULL)
5828 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5830 bResult = TRUE;
5831 lprc->left = ptItem.x + ptOrigin.x;
5832 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5833 ICON_BOTTOM_PADDING);
5834 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5835 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5837 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5838 lprc->right = lprc->left + nLabelWidth;
5840 else
5842 lprc->left += 1;
5843 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5844 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5849 else if (uView == LVS_SMALLICON)
5851 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5853 bResult = TRUE;
5854 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5855 lprc->top = ptItem.y + ptOrigin.y;
5856 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5858 if (infoPtr->himlState != NULL)
5859 lprc->left += infoPtr->iconSize.cx;
5861 if (infoPtr->himlSmall != NULL)
5862 lprc->left += infoPtr->iconSize.cx;
5864 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5865 nLabelWidth += TRAILING_PADDING;
5866 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5867 lprc->right = lprc->left + nLabelWidth;
5868 else
5869 lprc->right = nLeftPos + infoPtr->nItemWidth;
5872 else
5874 bResult = TRUE;
5875 if (uView == LVS_REPORT)
5876 nLeftPos = lprc->left = ptItem.x + nIndent;
5877 else
5878 nLeftPos = lprc->left = ptItem.x;
5879 lprc->top = ptItem.y;
5880 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5882 if (infoPtr->himlState != NULL)
5883 lprc->left += infoPtr->iconSize.cx;
5885 if (infoPtr->himlSmall != NULL)
5886 lprc->left += infoPtr->iconSize.cx;
5888 if (uView != LVS_REPORT)
5890 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5891 nLabelWidth += TRAILING_PADDING;
5892 if (infoPtr->himlSmall)
5893 nLabelWidth += IMAGE_PADDING;
5895 else
5896 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5897 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5898 lprc->right = lprc->left + nLabelWidth;
5899 else
5900 lprc->right = nLeftPos + infoPtr->nItemWidth;
5902 break;
5904 case LVIR_BOUNDS:
5905 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5906 ptItem.x = rcInternal.left;
5907 ptItem.y = rcInternal.top;
5908 if (uView == LVS_ICON)
5910 if (infoPtr->himlNormal != NULL)
5912 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5914 RECT label_rect;
5915 INT text_left, text_right, icon_left, text_pos_x;
5916 /* for style LVS_ICON bounds
5917 * left = min(icon.left, text.left)
5918 * right = max(icon.right, text.right)
5919 * top = boundbox.top + NOTHITABLE
5920 * bottom = text.bottom + 1
5922 bResult = TRUE;
5923 icon_left = text_left = ptItem.x;
5925 /* Correct ptItem to icon upper-left */
5926 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5927 ptItem.y += ICON_TOP_PADDING;
5929 /* Compute the label left and right */
5930 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5931 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5932 if (text_pos_x > 1)
5934 text_left += text_pos_x / 2;
5935 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5937 else
5939 text_left += 1;
5940 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5943 /* Compute rectangle w/o the text height */
5944 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5945 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5946 text_right) + ptOrigin.x;
5947 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5948 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5949 + infoPtr->iconSize.cy + 1
5950 + ICON_BOTTOM_PADDING;
5952 CopyRect (&label_rect, lprc);
5953 label_rect.top = lprc->bottom;
5954 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5955 UnionRect (lprc, lprc, &label_rect);
5959 else if (uView == LVS_SMALLICON)
5961 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5963 bResult = TRUE;
5964 lprc->left = ptItem.x + ptOrigin.x;
5965 lprc->right = lprc->left;
5966 lprc->top = ptItem.y + ptOrigin.y;
5967 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5968 if (infoPtr->himlState != NULL)
5969 lprc->right += infoPtr->iconSize.cx;
5970 if (infoPtr->himlSmall != NULL)
5971 lprc->right += infoPtr->iconSize.cx;
5973 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5974 nLabelWidth += TRAILING_PADDING;
5975 if (infoPtr->himlSmall)
5976 nLabelWidth += IMAGE_PADDING;
5977 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5978 lprc->right += nLabelWidth;
5979 else
5980 lprc->right = lprc->left + infoPtr->nItemWidth;
5983 else
5985 bResult = TRUE;
5986 lprc->left = ptItem.x;
5987 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5988 lprc->left += nIndent;
5989 lprc->right = lprc->left;
5990 lprc->top = ptItem.y;
5991 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5993 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5995 RECT br;
5996 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5997 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5999 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6001 else
6003 if (infoPtr->himlState != NULL)
6004 lprc->right += infoPtr->iconSize.cx;
6006 if (infoPtr->himlSmall != NULL)
6007 lprc->right += infoPtr->iconSize.cx;
6009 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6010 nLabelWidth += TRAILING_PADDING;
6011 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
6012 lprc->right += nLabelWidth;
6013 else
6014 lprc->right = lprc->left + infoPtr->nItemWidth;
6017 break;
6019 case LVIR_SELECTBOUNDS:
6020 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
6021 if (uView == LVS_ICON)
6023 if (infoPtr->himlNormal != NULL)
6025 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
6027 bResult = TRUE;
6028 lprc->left = ptItem.x + ptOrigin.x;
6029 lprc->top = ptItem.y + ptOrigin.y;
6030 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
6031 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
6035 else if (uView == LVS_SMALLICON)
6037 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
6039 bResult = TRUE;
6040 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
6041 lprc->top = ptItem.y + ptOrigin.y;
6042 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6044 if (infoPtr->himlState != NULL)
6045 lprc->left += infoPtr->iconSize.cx;
6047 lprc->right = lprc->left;
6049 if (infoPtr->himlSmall != NULL)
6050 lprc->right += infoPtr->iconSize.cx;
6052 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6053 nLabelWidth += TRAILING_PADDING;
6054 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6055 lprc->right += nLabelWidth;
6056 else
6057 lprc->right = nLeftPos + infoPtr->nItemWidth;
6060 else
6062 bResult = TRUE;
6063 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
6064 nLeftPos = lprc->left = ptItem.x + nIndent;
6065 else
6066 nLeftPos = lprc->left = ptItem.x;
6067 lprc->top = ptItem.y;
6068 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6070 if (infoPtr->himlState != NULL)
6071 lprc->left += infoPtr->iconSize.cx;
6073 lprc->right = lprc->left;
6075 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
6077 RECT br;
6078 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6079 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6081 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6083 else
6085 if (infoPtr->himlSmall != NULL)
6086 lprc->right += infoPtr->iconSize.cx;
6088 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6089 nLabelWidth += TRAILING_PADDING;
6090 if (infoPtr->himlSmall)
6091 nLabelWidth += IMAGE_PADDING;
6092 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6093 lprc->right += nLabelWidth;
6094 else
6095 lprc->right = nLeftPos + infoPtr->nItemWidth;
6098 break;
6102 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6103 lprc->left, lprc->top, lprc->right, lprc->bottom);
6105 return bResult;
6109 static LRESULT LISTVIEW_GetSubItemRect(HWND hwnd, INT nItem, INT nSubItem, INT
6110 flags, LPRECT lprc)
6112 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6113 INT count;
6115 TRACE("(hwnd=%x, nItem=%d, nSubItem=%d lprc=%p)\n", hwnd, nItem, nSubItem,
6116 lprc);
6118 if (!(uView & LVS_REPORT))
6119 return FALSE;
6121 if (flags & LVIR_ICON)
6123 FIXME("Unimplemented LVIR_ICON\n");
6124 return FALSE;
6126 else
6128 int top = min(((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0))->nColumnCount,
6129 nSubItem - 1);
6131 LISTVIEW_GetItemRect(hwnd,nItem,lprc);
6132 for (count = 0; count < top; count++)
6133 lprc->left += LISTVIEW_GetColumnWidth(hwnd,count);
6135 lprc->right = LISTVIEW_GetColumnWidth(hwnd,(nSubItem-1)) +
6136 lprc->left;
6138 return TRUE;
6142 /***
6143 * DESCRIPTION:
6144 * Retrieves the width of a label.
6146 * PARAMETER(S):
6147 * [I] HWND : window handle
6149 * RETURN:
6150 * SUCCESS : string width (in pixels)
6151 * FAILURE : zero
6153 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6155 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6156 INT nLabelWidth = 0;
6157 LVITEMW lvItem;
6159 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6161 ZeroMemory(&lvItem, sizeof(lvItem));
6162 lvItem.mask = LVIF_TEXT;
6163 lvItem.iItem = nItem;
6164 lvItem.cchTextMax = DISP_TEXT_SIZE;
6165 lvItem.pszText = szDispText;
6166 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6167 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
6169 return nLabelWidth;
6172 /***
6173 * DESCRIPTION:
6174 * Retrieves the spacing between listview control items.
6176 * PARAMETER(S):
6177 * [I] HWND : window handle
6178 * [I] BOOL : flag for small or large icon
6180 * RETURN:
6181 * Horizontal + vertical spacing
6183 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6185 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6186 LONG lResult;
6188 if (bSmall == FALSE)
6190 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6192 else
6194 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6195 if ((style & LVS_TYPEMASK) == LVS_ICON)
6196 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6197 else
6198 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6200 return lResult;
6203 /***
6204 * DESCRIPTION:
6205 * Retrieves the state of a listview control item.
6207 * PARAMETER(S):
6208 * [I] HWND : window handle
6209 * [I] INT : item index
6210 * [I] UINT : state mask
6212 * RETURN:
6213 * State specified by the mask.
6215 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6217 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6218 LVITEMW lvItem;
6219 UINT uState = 0;
6221 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6223 ZeroMemory(&lvItem, sizeof(lvItem));
6224 lvItem.iItem = nItem;
6225 lvItem.stateMask = uMask;
6226 lvItem.mask = LVIF_STATE;
6227 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6228 uState = lvItem.state;
6231 return uState;
6234 /***
6235 * DESCRIPTION:
6236 * Retrieves the text of a listview control item or subitem.
6238 * PARAMETER(S):
6239 * [I] hwnd : window handle
6240 * [I] nItem : item index
6241 * [IO] lpLVItem : item information
6242 * [I] isW : TRUE if lpLVItem is Unicode
6244 * RETURN:
6245 * SUCCESS : string length
6246 * FAILURE : 0
6248 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6250 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6251 INT nLength = 0;
6253 if (lpLVItem != NULL)
6255 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6257 lpLVItem->mask = LVIF_TEXT;
6258 lpLVItem->iItem = nItem;
6259 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6260 nLength = textlenT(lpLVItem->pszText, isW);
6264 return nLength;
6267 /***
6268 * DESCRIPTION:
6269 * Searches for an item based on properties + relationships.
6271 * PARAMETER(S):
6272 * [I] HWND : window handle
6273 * [I] INT : item index
6274 * [I] INT : relationship flag
6276 * RETURN:
6277 * SUCCESS : item index
6278 * FAILURE : -1
6280 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6282 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6283 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6284 UINT uMask = 0;
6285 LVFINDINFOW lvFindInfo;
6286 INT nCountPerColumn;
6287 INT i;
6289 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6291 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6293 if (uFlags & LVNI_CUT)
6294 uMask |= LVIS_CUT;
6296 if (uFlags & LVNI_DROPHILITED)
6297 uMask |= LVIS_DROPHILITED;
6299 if (uFlags & LVNI_FOCUSED)
6300 uMask |= LVIS_FOCUSED;
6302 if (uFlags & LVNI_SELECTED)
6303 uMask |= LVIS_SELECTED;
6305 if (uFlags & LVNI_ABOVE)
6307 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6309 while (nItem >= 0)
6311 nItem--;
6312 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6313 return nItem;
6316 else
6318 lvFindInfo.flags = LVFI_NEARESTXY;
6319 lvFindInfo.vkDirection = VK_UP;
6320 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6321 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6323 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6324 return nItem;
6328 else if (uFlags & LVNI_BELOW)
6330 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6332 while (nItem < GETITEMCOUNT(infoPtr))
6334 nItem++;
6335 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6336 return nItem;
6339 else
6341 lvFindInfo.flags = LVFI_NEARESTXY;
6342 lvFindInfo.vkDirection = VK_DOWN;
6343 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6344 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6346 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6347 return nItem;
6351 else if (uFlags & LVNI_TOLEFT)
6353 if (uView == LVS_LIST)
6355 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6356 while (nItem - nCountPerColumn >= 0)
6358 nItem -= nCountPerColumn;
6359 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6360 return nItem;
6363 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6365 lvFindInfo.flags = LVFI_NEARESTXY;
6366 lvFindInfo.vkDirection = VK_LEFT;
6367 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6368 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6370 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6371 return nItem;
6375 else if (uFlags & LVNI_TORIGHT)
6377 if (uView == LVS_LIST)
6379 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6380 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6382 nItem += nCountPerColumn;
6383 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6384 return nItem;
6387 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6389 lvFindInfo.flags = LVFI_NEARESTXY;
6390 lvFindInfo.vkDirection = VK_RIGHT;
6391 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6392 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6394 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6395 return nItem;
6399 else
6401 nItem++;
6403 /* search by index */
6404 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6406 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6407 return i;
6412 return -1;
6415 /* LISTVIEW_GetNumberOfWorkAreas */
6417 /***
6418 * DESCRIPTION:
6419 * Retrieves the origin coordinates when in icon or small icon display mode.
6421 * PARAMETER(S):
6422 * [I] HWND : window handle
6423 * [O] LPPOINT : coordinate information
6425 * RETURN:
6426 * SUCCESS : TRUE
6427 * FAILURE : FALSE
6429 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6431 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6432 UINT uView = lStyle & LVS_TYPEMASK;
6433 BOOL bResult = FALSE;
6435 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6437 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6439 SCROLLINFO scrollInfo;
6440 ZeroMemory(lpptOrigin, sizeof(POINT));
6441 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6442 scrollInfo.cbSize = sizeof(SCROLLINFO);
6444 if (lStyle & WS_HSCROLL)
6446 scrollInfo.fMask = SIF_POS;
6447 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6448 lpptOrigin->x = -scrollInfo.nPos;
6451 if (lStyle & WS_VSCROLL)
6453 scrollInfo.fMask = SIF_POS;
6454 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6455 lpptOrigin->y = -scrollInfo.nPos;
6458 bResult = TRUE;
6461 return bResult;
6464 /***
6465 * DESCRIPTION:
6466 * Retrieves the number of items that are marked as selected.
6468 * PARAMETER(S):
6469 * [I] HWND : window handle
6471 * RETURN:
6472 * Number of items selected.
6474 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6476 /* REDO THIS */
6477 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6478 INT nSelectedCount = 0;
6479 INT i;
6481 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6483 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6484 nSelectedCount++;
6487 return nSelectedCount;
6490 /***
6491 * DESCRIPTION:
6492 * Retrieves item index that marks the start of a multiple selection.
6494 * PARAMETER(S):
6495 * [I] HWND : window handle
6497 * RETURN:
6498 * Index number or -1 if there is no selection mark.
6500 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6502 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6504 return infoPtr->nSelectionMark;
6508 /***
6509 * DESCRIPTION:
6510 * Retrieves the width of a string.
6512 * PARAMETER(S):
6513 * [I] hwnd : window handle
6514 * [I] lpszText : text string to process
6515 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6517 * RETURN:
6518 * SUCCESS : string width (in pixels)
6519 * FAILURE : zero
6521 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6523 if (is_textT(lpszText, isW))
6525 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6526 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6527 HDC hdc = GetDC(hwnd);
6528 HFONT hOldFont = SelectObject(hdc, hFont);
6529 SIZE stringSize;
6530 ZeroMemory(&stringSize, sizeof(SIZE));
6531 if (isW)
6532 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6533 else
6534 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6535 SelectObject(hdc, hOldFont);
6536 ReleaseDC(hwnd, hdc);
6537 return stringSize.cx;
6539 return 0;
6542 /***
6543 * DESCRIPTION:
6544 * Retrieves the text backgound color.
6546 * PARAMETER(S):
6547 * [I] HWND : window handle
6549 * RETURN:
6550 * COLORREF associated with the the background.
6552 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6554 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6556 return infoPtr->clrTextBk;
6559 /***
6560 * DESCRIPTION:
6561 * Retrieves the text color.
6563 * PARAMETER(S):
6564 * [I] HWND : window handle
6566 * RETURN:
6567 * COLORREF associated with the text.
6569 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6571 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6573 return infoPtr->clrText;
6576 /***
6577 * DESCRIPTION:
6578 * Determines item if a hit or closest if not
6580 * PARAMETER(S):
6581 * [I] HWND : window handle
6582 * [IO] LPLV_INTHIT : hit test information
6583 * [I] subitem : fill out iSubItem.
6585 * RETURN:
6586 * SUCCESS : item index of hit
6587 * FAILURE : -1
6589 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6591 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6592 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6593 UINT uView = lStyle & LVS_TYPEMASK;
6594 INT i,j,topindex,bottomindex;
6595 RECT rcItem,rcSubItem;
6596 DWORD xterm, yterm, dist;
6598 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6600 topindex = ListView_GetTopIndex(hwnd);
6601 if (uView == LVS_REPORT)
6603 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6604 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6606 else
6608 bottomindex = GETITEMCOUNT(infoPtr);
6611 lpInt->distance = 0x7fffffff;
6612 lpInt->iDistItem = -1;
6614 for (i = topindex; i < bottomindex; i++)
6616 rcItem.left = LVIR_BOUNDS;
6617 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6619 if (PtInRect(&rcItem, lpInt->ht.pt))
6621 rcSubItem = rcItem;
6622 rcItem.left = LVIR_ICON;
6623 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6625 if (PtInRect(&rcItem, lpInt->ht.pt))
6627 lpInt->ht.flags = LVHT_ONITEMICON;
6628 lpInt->ht.iItem = i;
6629 goto set_subitem;
6633 rcItem.left = LVIR_LABEL;
6634 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6636 if (PtInRect(&rcItem, lpInt->ht.pt))
6638 lpInt->ht.flags = LVHT_ONITEMLABEL;
6639 lpInt->ht.iItem = i;
6640 goto set_subitem;
6644 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6645 lpInt->ht.iItem = i;
6646 set_subitem:
6647 if (subitem)
6649 lpInt->ht.iSubItem = 0;
6650 rcSubItem.right = rcSubItem.left;
6651 for (j = 0; j < infoPtr->nColumnCount; j++)
6653 rcSubItem.left = rcSubItem.right;
6654 rcSubItem.right += LISTVIEW_GetColumnWidth(hwnd, j);
6655 if (PtInRect(&rcSubItem, lpInt->ht.pt))
6657 lpInt->ht.iSubItem = j;
6658 break;
6662 return i;
6664 else
6667 * Now compute distance from point to center of boundary
6668 * box. Since we are only interested in the relative
6669 * distance, we can skip the nasty square root operation
6671 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6672 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6673 dist = xterm * xterm + yterm * yterm;
6674 if (dist < lpInt->distance)
6676 lpInt->distance = dist;
6677 lpInt->iDistItem = i;
6683 lpInt->ht.flags = LVHT_NOWHERE;
6684 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6686 return -1;
6689 /***
6690 * DESCRIPTION:
6691 * Determines which section of the item was selected (if any).
6693 * PARAMETER(S):
6694 * [I] HWND : window handle
6695 * [IO] LPLVHITTESTINFO : hit test information
6696 * [I] subitem : fill out iSubItem.
6698 * RETURN:
6699 * SUCCESS : item index
6700 * FAILURE : -1
6702 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6704 INT ret;
6705 LV_INTHIT lv_inthit;
6707 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6708 lpHitTestInfo->pt.y);
6710 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6711 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6712 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6713 return ret;
6716 /***
6717 * DESCRIPTION:
6718 * Determines which listview item is located at the specified position.
6720 * PARAMETER(S):
6721 * [I] HWND : window handle
6722 * [IO} LPLVHITTESTINFO : hit test information
6724 * RETURN:
6725 * SUCCESS : item index
6726 * FAILURE : -1
6728 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6730 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6731 INT nItem = -1;
6733 lpHitTestInfo->flags = 0;
6735 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6736 lpHitTestInfo->flags = LVHT_TOLEFT;
6737 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6738 lpHitTestInfo->flags = LVHT_TORIGHT;
6739 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6740 lpHitTestInfo->flags |= LVHT_ABOVE;
6741 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6742 lpHitTestInfo->flags |= LVHT_BELOW;
6744 if (lpHitTestInfo->flags == 0)
6746 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6747 * an app might pass only a structure with space up to iItem!
6748 * (MS Office 97 does that for instance in the file open dialog)
6750 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6753 return nItem;
6756 /***
6757 * DESCRIPTION:
6758 * Determines which listview subitem is located at the specified position.
6760 * PARAMETER(S):
6761 * [I] HWND : window handle
6762 * [IO} LPLVHITTESTINFO : hit test information
6764 * RETURN:
6765 * SUCCESS : item index
6766 * FAILURE : -1
6768 static LRESULT LISTVIEW_SubItemHitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6770 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6771 INT nItem = -1;
6773 lpHitTestInfo->flags = 0;
6775 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6776 lpHitTestInfo->flags = LVHT_TOLEFT;
6777 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6778 lpHitTestInfo->flags = LVHT_TORIGHT;
6779 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6780 lpHitTestInfo->flags |= LVHT_ABOVE;
6781 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6782 lpHitTestInfo->flags |= LVHT_BELOW;
6784 if (lpHitTestInfo->flags == 0)
6785 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, TRUE);
6787 return nItem;
6790 /***
6791 * DESCRIPTION:
6792 * Inserts a new column.
6794 * PARAMETER(S):
6795 * [I] HWND : window handle
6796 * [I] INT : column index
6797 * [I] LPLVCOLUMNW : column information
6799 * RETURN:
6800 * SUCCESS : new column index
6801 * FAILURE : -1
6803 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6804 LPLVCOLUMNW lpColumn, BOOL isW)
6806 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6807 INT nNewColumn = -1;
6808 HDITEMW hdi;
6810 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6812 if (lpColumn != NULL)
6814 /* initialize memory */
6815 ZeroMemory(&hdi, sizeof(hdi));
6817 if (lpColumn->mask & LVCF_FMT)
6819 /* format member is valid */
6820 hdi.mask |= HDI_FORMAT;
6822 /* set text alignment (leftmost column must be left-aligned) */
6823 if (nColumn == 0)
6825 hdi.fmt |= HDF_LEFT;
6827 else
6829 if (lpColumn->fmt & LVCFMT_LEFT)
6831 hdi.fmt |= HDF_LEFT;
6833 else if (lpColumn->fmt & LVCFMT_RIGHT)
6835 hdi.fmt |= HDF_RIGHT;
6837 else if (lpColumn->fmt & LVCFMT_CENTER)
6839 hdi.fmt |= HDF_CENTER;
6843 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6845 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6846 /* ??? */
6849 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6851 /* ??? */
6854 if (lpColumn->fmt & LVCFMT_IMAGE)
6856 hdi.fmt |= HDF_IMAGE;
6857 hdi.iImage = I_IMAGECALLBACK;
6861 if (lpColumn->mask & LVCF_WIDTH)
6863 hdi.mask |= HDI_WIDTH;
6864 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6866 /* make it fill the remainder of the controls width */
6867 HDITEMW hdit;
6868 RECT rcHeader;
6869 INT item_index;
6871 ZeroMemory(&hdit, sizeof(hdit));
6873 /* get the width of every item except the current one */
6874 hdit.mask = HDI_WIDTH;
6875 hdi.cxy = 0;
6877 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6878 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6879 hdi.cxy+=hdit.cxy;
6882 /* retrieve the layout of the header */
6883 GetClientRect(hwnd, &rcHeader);
6884 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6885 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6887 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6889 else
6890 hdi.cxy = lpColumn->cx;
6893 if (lpColumn->mask & LVCF_TEXT)
6895 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6896 hdi.pszText = lpColumn->pszText;
6897 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6898 hdi.fmt |= HDF_STRING;
6901 if (lpColumn->mask & LVCF_IMAGE)
6903 hdi.mask |= HDI_IMAGE;
6904 hdi.iImage = lpColumn->iImage;
6907 if (lpColumn->mask & LVCF_ORDER)
6909 hdi.mask |= HDI_ORDER;
6910 hdi.iOrder = lpColumn->iOrder;
6913 /* insert item in header control */
6914 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6915 (WPARAM)nColumn, (LPARAM)&hdi);
6917 /* Need to reset the item width when inserting a new column */
6918 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6920 LISTVIEW_UpdateScroll(hwnd);
6921 InvalidateRect(hwnd, NULL, FALSE);
6924 return nNewColumn;
6927 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6928 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6929 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6930 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6931 their own sort proc. when sending LVM_SORTITEMS.
6933 /* Platform SDK:
6934 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6936 LVS_SORTXXX must be specified,
6937 LVS_OWNERDRAW is not set,
6938 <item>.pszText is not LPSTR_TEXTCALLBACK.
6940 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6941 are sorted based on item text..."
6943 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6945 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6946 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6947 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6948 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6949 /* if we're sorting descending, negate the return value */
6950 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6953 /***
6954 * nESCRIPTION:
6955 * Inserts a new item in the listview control.
6957 * PARAMETER(S):
6958 * [I] HWND : window handle
6959 * [I] LPLVITEMW : item information
6960 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6962 * RETURN:
6963 * SUCCESS : new item index
6964 * FAILURE : -1
6966 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6968 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6969 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6970 UINT uView = lStyle & LVS_TYPEMASK;
6971 INT nItem = -1;
6972 HDPA hdpaSubItems;
6973 INT nItemWidth = 0;
6974 LISTVIEW_ITEM *lpItem = NULL;
6976 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6977 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6979 if (lStyle & LVS_OWNERDATA)
6981 nItem = infoPtr->hdpaItems->nItemCount;
6982 infoPtr->hdpaItems->nItemCount ++;
6983 return nItem;
6986 if (lpLVItem != NULL)
6988 /* make sure it's not a subitem; cannot insert a subitem */
6989 if (lpLVItem->iSubItem == 0)
6991 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6993 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6994 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6996 /* insert item in listview control data structure */
6997 if ( (hdpaSubItems = DPA_Create(8)) )
6999 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
7001 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
7002 && !(lStyle & LVS_OWNERDRAWFIXED)
7003 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
7005 /* Insert the item in the proper sort order based on the pszText
7006 member. See comments for LISTVIEW_InsertCompare() for greater detail */
7007 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
7008 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
7009 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
7010 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
7012 else
7014 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
7015 hdpaSubItems);
7017 if (nItem != -1)
7019 NMLISTVIEW nmlv;
7021 LISTVIEW_ShiftIndices(hwnd,nItem,1);
7023 /* manage item focus */
7024 if (lpLVItem->mask & LVIF_STATE)
7026 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
7027 if (lpLVItem->stateMask & LVIS_SELECTED)
7028 LISTVIEW_SetSelection(hwnd, nItem);
7029 else if (lpLVItem->stateMask & LVIS_FOCUSED)
7030 LISTVIEW_SetItemFocus(hwnd, nItem);
7033 /* send LVN_INSERTITEM notification */
7034 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7035 nmlv.iItem = nItem;
7036 nmlv.lParam = lpItem->lParam;
7037 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
7039 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
7041 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
7042 if (nItemWidth > infoPtr->nItemWidth)
7043 infoPtr->nItemWidth = nItemWidth;
7046 /* align items (set position of each item) */
7047 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
7049 if (lStyle & LVS_ALIGNLEFT)
7050 LISTVIEW_AlignLeft(hwnd);
7051 else
7052 LISTVIEW_AlignTop(hwnd);
7055 LISTVIEW_UpdateScroll(hwnd);
7056 /* refresh client area */
7057 InvalidateRect(hwnd, NULL, FALSE);
7066 /* free memory if unsuccessful */
7067 if ((nItem == -1) && (lpItem != NULL))
7068 COMCTL32_Free(lpItem);
7070 return nItem;
7073 /***
7074 * DESCRIPTION:
7075 * Redraws a range of items.
7077 * PARAMETER(S):
7078 * [I] HWND : window handle
7079 * [I] INT : first item
7080 * [I] INT : last item
7082 * RETURN:
7083 * SUCCESS : TRUE
7084 * FAILURE : FALSE
7086 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
7088 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7089 BOOL bResult = FALSE;
7090 RECT rcItem;
7091 INT i;
7093 if (nFirst <= nLast)
7095 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
7097 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
7099 for (i = nFirst; i <= nLast; i++)
7101 rcItem.left = LVIR_BOUNDS;
7102 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
7103 InvalidateRect(hwnd, &rcItem, TRUE);
7109 return bResult;
7112 /***
7113 * DESCRIPTION:
7114 * Scroll the content of a listview.
7116 * PARAMETER(S):
7117 * [I] HWND : window handle
7118 * [I] INT : horizontal scroll amount in pixels
7119 * [I] INT : vertical scroll amount in pixels
7121 * RETURN:
7122 * SUCCESS : TRUE
7123 * FAILURE : FALSE
7125 * COMMENTS:
7126 * If the control is in report mode (LVS_REPORT) the control can
7127 * be scrolled only in line increments. "dy" will be rounded to the
7128 * nearest number of pixels that are a whole line. Ex: if line height
7129 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7130 * is passed the the scroll will be 0. (per MSDN 7/2002)
7132 * For: (per experimentaion with native control and CSpy ListView)
7133 * LVS_ICON dy=1 = 1 pixel (vertical only)
7134 * dx ignored
7135 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
7136 * dx ignored
7137 * LVS_LIST dx=1 = 1 column (horizontal only)
7138 * but will only scroll 1 column per message
7139 * no matter what the value.
7140 * dy must be 0 or FALSE returned.
7141 * LVS_REPORT dx=1 = 1 pixel
7142 * dy= see above
7145 static LRESULT LISTVIEW_Scroll(HWND hwnd, INT dx, INT dy)
7147 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7148 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7149 UINT uView = lStyle & LVS_TYPEMASK;
7150 INT rows, mode, i;
7152 if (uView == LVS_REPORT)
7154 rows = (abs(dy) + infoPtr->nItemHeight/2) / infoPtr->nItemHeight;
7155 if (rows != 0)
7157 mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
7158 for ( i=0; i<rows; i++)
7159 LISTVIEW_VScroll(hwnd, mode, 0, hwnd);
7162 if (dx != 0)
7164 mode = (dx>0) ? SB_INTERNAL_RIGHT : SB_INTERNAL_LEFT;
7165 for ( i=0; i<abs(dx); i++)
7166 LISTVIEW_HScroll(hwnd, mode, 0, hwnd);
7168 return TRUE;
7170 else if (uView == LVS_ICON)
7172 INT mode, i;
7174 mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
7175 for(i=0; i<abs(dy); i++)
7176 LISTVIEW_VScroll(hwnd, mode, 0, hwnd);
7177 return TRUE;
7179 else if (uView == LVS_SMALLICON)
7181 INT mode, i;
7183 mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
7184 for(i=0; i<abs(dy); i++)
7185 LISTVIEW_VScroll(hwnd, mode, 0, hwnd);
7186 return TRUE;
7188 else if (uView == LVS_LIST)
7190 if (dy != 0) return FALSE;
7191 if (dx == 0) return TRUE;
7192 mode = (dx>0) ? SB_INTERNAL_RIGHT : SB_INTERNAL_LEFT;
7193 LISTVIEW_HScroll(hwnd, mode, 0, hwnd);
7194 return TRUE;
7196 return FALSE;
7199 /***
7200 * DESCRIPTION:
7201 * Sets the background color.
7203 * PARAMETER(S):
7204 * [I] HWND : window handle
7205 * [I] COLORREF : background color
7207 * RETURN:
7208 * SUCCESS : TRUE
7209 * FAILURE : FALSE
7211 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
7213 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7215 if(infoPtr->clrBk!=clrBk){
7216 infoPtr->clrBk = clrBk;
7217 InvalidateRect(hwnd, NULL, TRUE);
7220 return TRUE;
7223 /* LISTVIEW_SetBkImage */
7225 /***
7226 * DESCRIPTION:
7227 * Sets the callback mask. This mask will be used when the parent
7228 * window stores state information (some or all).
7230 * PARAMETER(S):
7231 * [I] HWND : window handle
7232 * [I] UINT : state mask
7234 * RETURN:
7235 * SUCCESS : TRUE
7236 * FAILURE : FALSE
7238 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
7240 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7242 infoPtr->uCallbackMask = uMask;
7244 return TRUE;
7247 /***
7248 * DESCRIPTION:
7249 * Sets the attributes of a header item.
7251 * PARAMETER(S):
7252 * [I] HWND : window handle
7253 * [I] INT : column index
7254 * [I] LPLVCOLUMNW : column attributes
7255 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
7256 * otherwise it is in fact a LPLVCOLUMNA
7258 * RETURN:
7259 * SUCCESS : TRUE
7260 * FAILURE : FALSE
7262 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
7263 LPLVCOLUMNW lpColumn, BOOL isW)
7265 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7266 BOOL bResult = FALSE;
7267 HDITEMW hdi, hdiget;
7269 if ((lpColumn != NULL) && (nColumn >= 0) &&
7270 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7272 /* initialize memory */
7273 ZeroMemory(&hdi, sizeof(hdi));
7275 if (lpColumn->mask & LVCF_FMT)
7277 /* format member is valid */
7278 hdi.mask |= HDI_FORMAT;
7280 /* get current format first */
7281 hdiget.mask = HDI_FORMAT;
7282 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7283 /* preserve HDF_STRING if present */
7284 hdi.fmt = hdiget.fmt & HDF_STRING;
7286 /* set text alignment (leftmost column must be left-aligned) */
7287 if (nColumn == 0)
7289 hdi.fmt |= HDF_LEFT;
7291 else
7293 if (lpColumn->fmt & LVCFMT_LEFT)
7294 hdi.fmt |= HDF_LEFT;
7295 else if (lpColumn->fmt & LVCFMT_RIGHT)
7296 hdi.fmt |= HDF_RIGHT;
7297 else if (lpColumn->fmt & LVCFMT_CENTER)
7298 hdi.fmt |= HDF_CENTER;
7301 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7302 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7304 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7305 hdi.fmt |= HDF_IMAGE;
7307 if (lpColumn->fmt & LVCFMT_IMAGE)
7309 hdi.fmt |= HDF_IMAGE;
7310 hdi.iImage = I_IMAGECALLBACK;
7314 if (lpColumn->mask & LVCF_WIDTH)
7316 hdi.mask |= HDI_WIDTH;
7317 hdi.cxy = lpColumn->cx;
7320 if (lpColumn->mask & LVCF_TEXT)
7322 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7323 hdi.pszText = lpColumn->pszText;
7324 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7325 hdi.fmt |= HDF_STRING;
7328 if (lpColumn->mask & LVCF_IMAGE)
7330 hdi.mask |= HDI_IMAGE;
7331 hdi.iImage = lpColumn->iImage;
7334 if (lpColumn->mask & LVCF_ORDER)
7336 hdi.mask |= HDI_ORDER;
7337 hdi.iOrder = lpColumn->iOrder;
7340 /* set header item attributes */
7341 if (isW)
7342 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7343 else
7344 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
7347 return bResult;
7350 /***
7351 * DESCRIPTION:
7352 * Sets the column order array
7354 * PARAMETERS:
7355 * [I] HWND : window handle
7356 * [I] INT : number of elements in column order array
7357 * [I] INT : pointer to column order array
7359 * RETURN:
7360 * SUCCESS : TRUE
7361 * FAILURE : FALSE
7363 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7365 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7367 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7369 if (!lpiArray)
7370 return FALSE;
7372 return TRUE;
7376 /***
7377 * DESCRIPTION:
7378 * Sets the width of a column
7380 * PARAMETERS:
7381 * [I] HWND : window handle
7382 * [I] INT : column index
7383 * [I] INT : column width
7385 * RETURN:
7386 * SUCCESS : TRUE
7387 * FAILURE : FALSE
7389 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7391 LISTVIEW_INFO *infoPtr;
7392 HDITEMW hdi;
7393 LRESULT lret;
7394 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7395 UINT uView = lStyle & LVS_TYPEMASK;
7396 HDC hdc;
7397 HFONT header_font;
7398 HFONT old_font;
7399 SIZE size;
7400 WCHAR text_buffer[DISP_TEXT_SIZE];
7401 INT header_item_count;
7402 INT item_index;
7403 INT nLabelWidth;
7404 RECT rcHeader;
7405 LVITEMW lvItem;
7406 WCHAR szDispText[DISP_TEXT_SIZE];
7408 /* make sure we can get the listview info */
7409 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7410 return (FALSE);
7412 if (!infoPtr->hwndHeader) /* make sure we have a header */
7413 return (FALSE);
7415 /* set column width only if in report or list mode */
7416 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7417 return (FALSE);
7419 TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7421 /* take care of invalid cx values */
7422 if((uView == LVS_REPORT) && (cx < -2))
7423 cx = LVSCW_AUTOSIZE;
7424 else if (uView == LVS_LIST && (cx < 1))
7425 return FALSE;
7427 /* resize all columns if in LVS_LIST mode */
7428 if(uView == LVS_LIST) {
7429 infoPtr->nItemWidth = cx;
7430 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7431 return TRUE;
7434 /* autosize based on listview items width */
7435 if(cx == LVSCW_AUTOSIZE)
7437 /* set the width of the column to the width of the widest item */
7438 if (iCol == 0 || uView == LVS_LIST)
7440 cx = 0;
7441 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7443 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7444 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7446 if (infoPtr->himlSmall)
7447 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7449 else
7451 ZeroMemory(&lvItem, sizeof(lvItem));
7452 lvItem.iSubItem = iCol;
7453 lvItem.mask = LVIF_TEXT;
7454 lvItem.cchTextMax = DISP_TEXT_SIZE;
7455 lvItem.pszText = szDispText;
7456 *lvItem.pszText = '\0';
7457 cx = 0;
7458 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7460 lvItem.iItem = item_index;
7461 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7462 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7463 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7466 cx += TRAILING_PADDING;
7467 } /* autosize based on listview header width */
7468 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7470 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7472 /* if iCol is the last column make it fill the remainder of the controls width */
7473 if(iCol == (header_item_count - 1)) {
7474 /* get the width of every item except the current one */
7475 hdi.mask = HDI_WIDTH;
7476 cx = 0;
7478 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7479 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7480 cx+=hdi.cxy;
7483 /* retrieve the layout of the header */
7484 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7486 cx = (rcHeader.right - rcHeader.left) - cx;
7488 else
7490 /* Despite what the MS docs say, if this is not the last
7491 column, then MS resizes the column to the width of the
7492 largest text string in the column, including headers
7493 and items. This is different from LVSCW_AUTOSIZE in that
7494 LVSCW_AUTOSIZE ignores the header string length.
7497 /* retrieve header font */
7498 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7500 /* retrieve header text */
7501 hdi.mask = HDI_TEXT;
7502 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7503 hdi.pszText = text_buffer;
7505 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7507 /* determine the width of the text in the header */
7508 hdc = GetDC(hwnd);
7509 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7511 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7513 SelectObject(hdc, old_font); /* restore the old font */
7514 ReleaseDC(hwnd, hdc);
7516 ZeroMemory(&lvItem, sizeof(lvItem));
7517 lvItem.iSubItem = iCol;
7518 lvItem.mask = LVIF_TEXT;
7519 lvItem.cchTextMax = DISP_TEXT_SIZE;
7520 lvItem.pszText = szDispText;
7521 *lvItem.pszText = '\0';
7522 cx = size.cx;
7523 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7525 lvItem.iItem = item_index;
7526 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7527 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7528 nLabelWidth += TRAILING_PADDING;
7529 /* While it is possible for subitems to have icons, even MS messes
7530 up the positioning, so I suspect no applications actually use
7531 them. */
7532 if (item_index == 0 && infoPtr->himlSmall)
7533 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7534 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7539 /* call header to update the column change */
7540 hdi.mask = HDI_WIDTH;
7542 hdi.cxy = cx;
7543 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7545 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7547 return lret;
7550 /***
7551 * DESCRIPTION:
7552 * Sets the extended listview style.
7554 * PARAMETERS:
7555 * [I] HWND : window handle
7556 * [I] DWORD : mask
7557 * [I] DWORD : style
7559 * RETURN:
7560 * SUCCESS : previous style
7561 * FAILURE : 0
7563 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7565 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7566 DWORD dwOldStyle = infoPtr->dwExStyle;
7568 /* set new style */
7569 if (dwMask)
7570 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7571 else
7572 infoPtr->dwExStyle = dwStyle;
7574 return dwOldStyle;
7577 /* LISTVIEW_SetHotCursor */
7579 /***
7580 * DESCRIPTION:
7581 * Sets the hot item index.
7583 * PARAMETERS:
7584 * [I] HWND : window handle
7585 * [I] INT : index
7587 * RETURN:
7588 * SUCCESS : previous hot item index
7589 * FAILURE : -1 (no hot item)
7591 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7593 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7594 INT iOldIndex = infoPtr->nHotItem;
7596 /* set new style */
7597 infoPtr->nHotItem = iIndex;
7599 return iOldIndex;
7602 /***
7603 * DESCRIPTION:
7604 * Sets the amount of time the cursor must hover over an item before it is selected.
7606 * PARAMETER(S):
7607 * [I] HWND : window handle
7608 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7610 * RETURN:
7611 * Returns the previous hover time
7613 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7615 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7616 DWORD oldHoverTime = infoPtr->dwHoverTime;
7618 infoPtr->dwHoverTime = dwHoverTime;
7620 return oldHoverTime;
7623 /***
7624 * DESCRIPTION:
7625 * Sets spacing for icons of LVS_ICON style.
7627 * PARAMETER(S):
7628 * [I] HWND : window handle
7629 * [I] DWORD : MAKELONG(cx, cy)
7631 * RETURN:
7632 * MAKELONG(oldcx, oldcy)
7634 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7636 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7637 INT cy = HIWORD(spacing);
7638 INT cx = LOWORD(spacing);
7639 DWORD oldspacing;
7640 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7641 UINT uView = lStyle & LVS_TYPEMASK;
7643 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7644 if (cx == -1) /* set to default */
7645 cx = GetSystemMetrics(SM_CXICONSPACING);
7646 if (cy == -1) /* set to default */
7647 cy = GetSystemMetrics(SM_CYICONSPACING);
7649 if (cx)
7650 infoPtr->iconSpacing.cx = cx;
7651 else
7652 { /* if 0 then compute width */
7653 if (uView == LVS_ICON)
7654 FIXME("width computation not yet done\n");
7656 * Should scan each item and determine max width of
7657 * icon or label, then make that the width
7659 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7660 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7662 if (cy)
7663 infoPtr->iconSpacing.cy = cy;
7664 else
7665 { /* if 0 then compute height */
7666 if (uView == LVS_ICON)
7667 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7668 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7669 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7670 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7671 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7674 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7675 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7677 /* these depend on the iconSpacing */
7678 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7679 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7681 return oldspacing;
7684 /***
7685 * DESCRIPTION:
7686 * Sets image lists.
7688 * PARAMETER(S):
7689 * [I] HWND : window handle
7690 * [I] INT : image list type
7691 * [I] HIMAGELIST : image list handle
7693 * RETURN:
7694 * SUCCESS : old image list
7695 * FAILURE : NULL
7697 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7699 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7700 HIMAGELIST himlOld = 0;
7701 INT oldHeight;
7703 switch (nType)
7705 case LVSIL_NORMAL:
7706 himlOld = infoPtr->himlNormal;
7707 infoPtr->himlNormal = himl;
7708 break;
7710 case LVSIL_SMALL:
7711 himlOld = infoPtr->himlSmall;
7712 infoPtr->himlSmall = himl;
7713 break;
7715 case LVSIL_STATE:
7716 himlOld = infoPtr->himlState;
7717 infoPtr->himlState = himl;
7718 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7719 break;
7722 oldHeight = infoPtr->nItemHeight;
7723 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7724 if (infoPtr->nItemHeight != oldHeight)
7725 LISTVIEW_UpdateScroll(hwnd);
7727 return himlOld;
7730 /***
7731 * DESCRIPTION:
7732 * Preallocates memory (does *not* set the actual count of items !)
7734 * PARAMETER(S):
7735 * [I] HWND : window handle
7736 * [I] INT : item count (projected number of items to allocate)
7737 * [I] DWORD : update flags
7739 * RETURN:
7740 * SUCCESS : TRUE
7741 * FAILURE : FALSE
7743 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7745 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7747 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7749 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7751 int precount,topvisible;
7753 TRACE("LVS_OWNERDATA is set!\n");
7754 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7755 FIXME("flags %s %s not implemented\n",
7756 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7757 : "",
7758 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7761 * Internally remove all the selections.
7765 LISTVIEW_SELECTION *selection;
7766 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7767 if (selection)
7768 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7769 selection->upper);
7771 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7773 precount = infoPtr->hdpaItems->nItemCount;
7774 topvisible = ListView_GetTopIndex(hwnd) +
7775 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7777 infoPtr->hdpaItems->nItemCount = nItems;
7779 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(hwnd),
7780 DEFAULT_COLUMN_WIDTH);
7782 LISTVIEW_UpdateSize(hwnd);
7783 LISTVIEW_UpdateScroll(hwnd);
7785 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7786 InvalidateRect(hwnd, NULL, TRUE);
7788 else
7790 /* According to MSDN for non-LVS_OWNERDATA this is just
7791 * a performance issue. The control allocates its internal
7792 * data structures for the number of items specified. It
7793 * cuts down on the number of memory allocations. Therefore
7794 * we will just issue a WARN here
7796 WARN("for non-ownerdata performance option not implemented.\n");
7799 return TRUE;
7802 /***
7803 * DESCRIPTION:
7804 * Sets the position of an item.
7806 * PARAMETER(S):
7807 * [I] HWND : window handle
7808 * [I] INT : item index
7809 * [I] LONG : x coordinate
7810 * [I] LONG : y coordinate
7812 * RETURN:
7813 * SUCCESS : TRUE
7814 * FAILURE : FALSE
7816 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7817 LONG nPosX, LONG nPosY)
7819 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7820 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7821 UINT uView = lStyle & LVS_TYPEMASK;
7822 LISTVIEW_ITEM *lpItem;
7823 HDPA hdpaSubItems;
7824 BOOL bResult = FALSE;
7826 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7828 if (lStyle & LVS_OWNERDATA)
7829 return FALSE;
7831 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7833 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7835 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7837 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7839 POINT orig;
7840 bResult = TRUE;
7841 orig = lpItem->ptPosition;
7842 if ((nPosX == -1) && (nPosY == -1))
7844 /* This point value seems to be an undocumented feature. The
7845 * best guess is that it means either at the origin, or at
7846 * the true beginning of the list. I will assume the origin.
7848 POINT pt1;
7849 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7851 pt1.x = 0;
7852 pt1.y = 0;
7854 nPosX = pt1.x;
7855 nPosY = pt1.y;
7856 if (uView == LVS_ICON)
7858 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7859 nPosY += ICON_TOP_PADDING;
7861 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7862 nPosX, nPosY);
7865 lpItem->ptPosition.x = nPosX;
7866 lpItem->ptPosition.y = nPosY;
7867 if (uView == LVS_ICON)
7869 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7870 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7871 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7873 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7874 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7877 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7878 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7881 else
7883 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7884 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7892 return bResult;
7895 /***
7896 * DESCRIPTION:
7897 * Sets the state of one or many items.
7899 * PARAMETER(S):
7900 * [I] HWND : window handle
7901 * [I]INT : item index
7902 * [I] LPLVITEM : item or subitem info
7904 * RETURN:
7905 * SUCCESS : TRUE
7906 * FAILURE : FALSE
7908 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7910 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7911 BOOL bResult = TRUE;
7912 LVITEMW lvItem;
7914 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7915 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7917 ZeroMemory(&lvItem, sizeof(lvItem));
7918 lvItem.mask = LVIF_STATE;
7919 lvItem.state = lpLVItem->state;
7920 lvItem.stateMask = lpLVItem->stateMask ;
7921 lvItem.iItem = nItem;
7923 if (nItem == -1)
7925 /* apply to all items */
7926 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7927 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7929 else
7930 bResult = ListView_SetItemW(hwnd, &lvItem);
7932 return bResult;
7935 /***
7936 * DESCRIPTION:
7937 * Sets the text of an item or subitem.
7939 * PARAMETER(S):
7940 * [I] hwnd : window handle
7941 * [I] nItem : item index
7942 * [I] lpLVItem : item or subitem info
7943 * [I] isW : TRUE if input is Unicode
7945 * RETURN:
7946 * SUCCESS : TRUE
7947 * FAILURE : FALSE
7949 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7951 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7952 BOOL bResult = FALSE;
7953 LVITEMW lvItem;
7955 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7956 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7958 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7960 ZeroMemory(&lvItem, sizeof(LVITEMW));
7961 lvItem.mask = LVIF_TEXT;
7962 lvItem.pszText = lpLVItem->pszText;
7963 lvItem.iItem = nItem;
7964 lvItem.iSubItem = lpLVItem->iSubItem;
7965 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7966 else bResult = ListView_SetItemA(hwnd, &lvItem);
7969 return bResult;
7972 /***
7973 * DESCRIPTION:
7974 * Set item index that marks the start of a multiple selection.
7976 * PARAMETER(S):
7977 * [I] HWND : window handle
7978 * [I] INT : index
7980 * RETURN:
7981 * Index number or -1 if there is no selection mark.
7983 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7985 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7986 INT nOldIndex = infoPtr->nSelectionMark;
7988 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7990 infoPtr->nSelectionMark = nIndex;
7992 return nOldIndex;
7995 /***
7996 * DESCRIPTION:
7997 * Sets the text background color.
7999 * PARAMETER(S):
8000 * [I] HWND : window handle
8001 * [I] COLORREF : text background color
8003 * RETURN:
8004 * SUCCESS : TRUE
8005 * FAILURE : FALSE
8007 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
8009 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8011 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
8013 infoPtr->clrTextBk = clrTextBk;
8014 InvalidateRect(hwnd, NULL, TRUE);
8016 return TRUE;
8019 /***
8020 * DESCRIPTION:
8021 * Sets the text foreground color.
8023 * PARAMETER(S):
8024 * [I] HWND : window handle
8025 * [I] COLORREF : text color
8027 * RETURN:
8028 * SUCCESS : TRUE
8029 * FAILURE : FALSE
8031 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
8033 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8035 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
8037 infoPtr->clrText = clrText;
8038 InvalidateRect(hwnd, NULL, TRUE);
8040 return TRUE;
8043 /* LISTVIEW_SetToolTips */
8044 /* LISTVIEW_SetUnicodeFormat */
8045 /* LISTVIEW_SetWorkAreas */
8047 /***
8048 * DESCRIPTION:
8049 * Callback internally used by LISTVIEW_SortItems()
8051 * PARAMETER(S):
8052 * [I] LPVOID : first LISTVIEW_ITEM to compare
8053 * [I] LPVOID : second LISTVIEW_ITEM to compare
8054 * [I] LPARAM : HWND of control
8056 * RETURN:
8057 * if first comes before second : negative
8058 * if first comes after second : positive
8059 * if first and second are equivalent : zero
8061 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8063 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
8064 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
8065 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
8067 /* Forward the call to the client defined callback */
8068 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8071 /***
8072 * DESCRIPTION:
8073 * Sorts the listview items.
8075 * PARAMETER(S):
8076 * [I] HWND : window handle
8077 * [I] WPARAM : application-defined value
8078 * [I] LPARAM : pointer to comparision callback
8080 * RETURN:
8081 * SUCCESS : TRUE
8082 * FAILURE : FALSE
8084 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
8086 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8087 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8088 HDPA hdpaSubItems=NULL;
8089 LISTVIEW_ITEM *pLVItem=NULL;
8090 LPVOID selectionMarkItem;
8091 int nCount, i;
8093 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
8095 if (lStyle & LVS_OWNERDATA) return FALSE;
8097 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
8099 nCount = GETITEMCOUNT(infoPtr);
8100 /* if there are 0 or 1 items, there is no need to sort */
8101 if (nCount < 2)
8102 return TRUE;
8104 infoPtr->pfnCompare = pfnCompare;
8105 infoPtr->lParamSort = lParamSort;
8106 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
8108 /* Adjust selections and indices so that they are the way they should
8109 * be after the sort (otherwise, the list items move around, but
8110 * whatever is at the item's previous original position will be
8111 * selected instead)
8113 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
8114 for (i=0; i < nCount; i++)
8116 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
8117 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
8119 if (pLVItem->state & LVIS_SELECTED)
8120 LISTVIEW_AddSelectionRange(hwnd, i, i);
8121 else
8122 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
8123 if (pLVItem->state & LVIS_FOCUSED)
8124 infoPtr->nFocusedItem=i;
8126 if (selectionMarkItem != NULL)
8127 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8128 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8130 /* align the items */
8131 LISTVIEW_AlignTop(hwnd);
8133 /* refresh the display */
8134 InvalidateRect(hwnd, NULL, TRUE);
8136 return TRUE;
8139 /* LISTVIEW_SubItemHitTest */
8141 /***
8142 * DESCRIPTION:
8143 * Updates an items or rearranges the listview control.
8145 * PARAMETER(S):
8146 * [I] HWND : window handle
8147 * [I] INT : item index
8149 * RETURN:
8150 * SUCCESS : TRUE
8151 * FAILURE : FALSE
8153 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
8155 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8156 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8157 BOOL bResult = FALSE;
8158 RECT rc;
8160 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
8162 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8164 bResult = TRUE;
8166 /* rearrange with default alignment style */
8167 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
8168 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
8170 ListView_Arrange(hwnd, 0);
8172 else
8174 /* get item bounding rectangle */
8175 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
8176 InvalidateRect(hwnd, &rc, TRUE);
8180 return bResult;
8183 /***
8184 * DESCRIPTION:
8185 * Creates the listview control.
8187 * PARAMETER(S):
8188 * [I] HWND : window handle
8190 * RETURN:
8191 * Zero
8193 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
8195 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8196 UINT uView = lpcs->style & LVS_TYPEMASK;
8197 LOGFONTW logFont;
8199 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
8201 /* initialize info pointer */
8202 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
8204 /* determine the type of structures to use */
8205 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
8206 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8208 /* initialize color information */
8209 infoPtr->clrBk = comctl32_color.clrWindow;
8210 infoPtr->clrText = comctl32_color.clrWindowText;
8211 infoPtr->clrTextBk = CLR_DEFAULT;
8213 /* set default values */
8214 infoPtr->hwndSelf = hwnd;
8215 infoPtr->uCallbackMask = 0;
8216 infoPtr->nFocusedItem = -1;
8217 infoPtr->nSelectionMark = -1;
8218 infoPtr->nHotItem = -1;
8219 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8220 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8221 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
8222 infoPtr->hwndEdit = 0;
8223 infoPtr->pedititem = NULL;
8224 infoPtr->nEditLabelItem = -1;
8225 infoPtr->bIsDrawing = FALSE;
8227 /* get default font (icon title) */
8228 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8229 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8230 infoPtr->hFont = infoPtr->hDefaultFont;
8231 LISTVIEW_SaveTextMetrics(hwnd);
8233 /* create header */
8234 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
8235 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
8236 0, 0, 0, 0, hwnd, (HMENU)0,
8237 lpcs->hInstance, NULL);
8239 /* set header unicode format */
8240 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
8242 /* set header font */
8243 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
8244 (LPARAM)TRUE);
8246 if (uView == LVS_ICON)
8248 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8249 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8251 else if (uView == LVS_REPORT)
8253 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8255 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8257 else
8259 /* set HDS_HIDDEN flag to hide the header bar */
8260 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
8261 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8265 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8266 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8268 else
8270 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8271 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8274 /* display unsupported listview window styles */
8275 LISTVIEW_UnsupportedStyles(lpcs->style);
8277 /* allocate memory for the data structure */
8278 infoPtr->hdpaItems = DPA_Create(10);
8280 /* allocate memory for the selection ranges */
8281 infoPtr->hdpaSelectionRanges = DPA_Create(10);
8283 /* initialize size of items */
8284 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8285 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8287 /* initialize the hover time to -1(indicating the default system hover time) */
8288 infoPtr->dwHoverTime = -1;
8290 return 0;
8293 /***
8294 * DESCRIPTION:
8295 * Erases the background of the listview control.
8297 * PARAMETER(S):
8298 * [I] HWND : window handle
8299 * [I] WPARAM : device context handle
8300 * [I] LPARAM : not used
8302 * RETURN:
8303 * SUCCESS : TRUE
8304 * FAILURE : FALSE
8306 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
8307 LPARAM lParam)
8309 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8310 BOOL bResult;
8312 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8314 if (infoPtr->clrBk == CLR_NONE)
8316 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8318 else
8320 RECT rc;
8321 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8322 GetClientRect(hwnd, &rc);
8323 FillRect((HDC)wParam, &rc, hBrush);
8324 DeleteObject(hBrush);
8325 bResult = TRUE;
8328 return bResult;
8332 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8334 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8336 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8338 if (infoPtr->clrBk != CLR_NONE)
8340 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8341 FillRect(hdc, rc, hBrush);
8342 DeleteObject(hBrush);
8346 /***
8347 * DESCRIPTION:
8348 * Retrieves the listview control font.
8350 * PARAMETER(S):
8351 * [I] HWND : window handle
8353 * RETURN:
8354 * Font handle.
8356 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8358 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8360 TRACE("(hwnd=%x)\n", hwnd);
8362 return infoPtr->hFont;
8365 /***
8366 * DESCRIPTION:
8367 * Performs vertical scrolling.
8369 * PARAMETER(S):
8370 * [I] HWND : window handle
8371 * [I] INT : scroll code
8372 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8373 * or SB_THUMBTRACK.
8374 * [I] HWND : scrollbar control window handle
8376 * RETURN:
8377 * Zero
8379 * NOTES:
8380 * SB_LINEUP/SB_LINEDOWN:
8381 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8382 * for LVS_REPORT is 1 line --> infoPtr->nItemHeight
8383 * for LVS_LIST cannot occur ??? (implemented as LVS_REPORT)
8386 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8387 HWND hScrollWnd)
8389 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8390 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8391 SCROLLINFO scrollInfo;
8392 BOOL is_an_icon;
8394 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8395 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8397 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8399 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8400 scrollInfo.cbSize = sizeof(SCROLLINFO);
8401 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8403 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8405 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8407 INT nOldScrollPos = scrollInfo.nPos;
8408 switch (nScrollCode)
8410 case SB_INTERNAL_UP:
8411 if (scrollInfo.nPos > scrollInfo.nMin)
8412 scrollInfo.nPos--;
8413 break;
8415 case SB_INTERNAL_DOWN:
8416 if (scrollInfo.nPos < scrollInfo.nMax)
8417 scrollInfo.nPos++;
8418 break;
8420 case SB_LINEUP:
8421 if (scrollInfo.nPos > scrollInfo.nMin)
8422 scrollInfo.nPos -= (is_an_icon) ?
8423 LISTVIEW_SCROLL_ICON_LINE_SIZE : infoPtr->nItemHeight;
8424 break;
8426 case SB_LINEDOWN:
8427 if (scrollInfo.nPos < scrollInfo.nMax)
8428 scrollInfo.nPos += (is_an_icon) ?
8429 LISTVIEW_SCROLL_ICON_LINE_SIZE : infoPtr->nItemHeight;
8430 break;
8432 case SB_PAGEUP:
8433 if (scrollInfo.nPos > scrollInfo.nMin)
8435 if (scrollInfo.nPos >= scrollInfo.nPage)
8436 scrollInfo.nPos -= scrollInfo.nPage;
8437 else
8438 scrollInfo.nPos = scrollInfo.nMin;
8440 break;
8442 case SB_PAGEDOWN:
8443 if (scrollInfo.nPos < scrollInfo.nMax)
8445 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8446 scrollInfo.nPos += scrollInfo.nPage;
8447 else
8448 scrollInfo.nPos = scrollInfo.nMax;
8450 break;
8452 case SB_THUMBPOSITION:
8453 case SB_THUMBTRACK:
8454 scrollInfo.nPos = nCurrentPos;
8455 if (scrollInfo.nPos > scrollInfo.nMax)
8456 scrollInfo.nPos=scrollInfo.nMax;
8458 if (scrollInfo.nPos < scrollInfo.nMin)
8459 scrollInfo.nPos=scrollInfo.nMin;
8461 break;
8464 if (nOldScrollPos != scrollInfo.nPos)
8466 scrollInfo.fMask = SIF_POS;
8467 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8469 /* Get real position value, the value we set might have been changed
8470 * by SetScrollInfo (especially if we went too far.
8472 scrollInfo.fMask = SIF_POS;
8473 GetScrollInfo(hwnd, SB_VERT, &scrollInfo);
8475 /* only if the scroll position really changed, do we update screen */
8476 if (nOldScrollPos != scrollInfo.nPos)
8478 if (IsWindowVisible(infoPtr->hwndHeader))
8480 RECT rListview, rcHeader, rDest;
8481 GetClientRect(hwnd, &rListview);
8482 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8483 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8484 SubtractRect(&rDest, &rListview, &rcHeader);
8485 InvalidateRect(hwnd, &rDest, TRUE);
8487 else
8488 InvalidateRect(hwnd, NULL, TRUE);
8493 return 0;
8496 /***
8497 * DESCRIPTION:
8498 * Performs horizontal scrolling.
8500 * PARAMETER(S):
8501 * [I] HWND : window handle
8502 * [I] INT : scroll code
8503 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8504 * or SB_THUMBTRACK.
8505 * [I] HWND : scrollbar control window handle
8507 * RETURN:
8508 * Zero
8510 * NOTES:
8511 * SB_LINELEFT/SB_LINERIGHT:
8512 * for LVS_ICON, LVS_SMALLICON ??? (implemented as 1 pixel)
8513 * for LVS_REPORT is 1 pixel
8514 * for LVS_LIST is 1 column --> which is a 1 because the
8515 * scroll is based on columns not pixels
8518 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8519 HWND hScrollWnd)
8521 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8522 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8523 SCROLLINFO scrollInfo;
8524 BOOL is_a_list;
8526 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8527 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8529 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8531 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8532 scrollInfo.cbSize = sizeof(SCROLLINFO);
8533 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8535 is_a_list = (uView == LVS_LIST);
8537 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8539 INT nOldScrollPos = scrollInfo.nPos;
8541 switch (nScrollCode)
8543 case SB_INTERNAL_LEFT:
8544 if (scrollInfo.nPos > scrollInfo.nMin)
8545 scrollInfo.nPos--;
8546 break;
8548 case SB_INTERNAL_RIGHT:
8549 if (scrollInfo.nPos < scrollInfo.nMax)
8550 scrollInfo.nPos++;
8551 break;
8553 case SB_LINELEFT:
8554 if (scrollInfo.nPos > scrollInfo.nMin)
8555 scrollInfo.nPos--;
8556 break;
8558 case SB_LINERIGHT:
8559 if (scrollInfo.nPos < scrollInfo.nMax)
8560 scrollInfo.nPos++;
8561 break;
8563 case SB_PAGELEFT:
8564 if (scrollInfo.nPos > scrollInfo.nMin)
8566 if (scrollInfo.nPos >= scrollInfo.nPage)
8567 scrollInfo.nPos -= scrollInfo.nPage;
8568 else
8569 scrollInfo.nPos = scrollInfo.nMin;
8571 break;
8573 case SB_PAGERIGHT:
8574 if (scrollInfo.nPos < scrollInfo.nMax)
8576 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8577 scrollInfo.nPos += scrollInfo.nPage;
8578 else
8579 scrollInfo.nPos = scrollInfo.nMax;
8581 break;
8583 case SB_THUMBPOSITION:
8584 case SB_THUMBTRACK:
8585 scrollInfo.nPos = nCurrentPos;
8587 if (scrollInfo.nPos > scrollInfo.nMax)
8588 scrollInfo.nPos=scrollInfo.nMax;
8590 if (scrollInfo.nPos < scrollInfo.nMin)
8591 scrollInfo.nPos=scrollInfo.nMin;
8592 break;
8595 if (nOldScrollPos != scrollInfo.nPos)
8597 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8598 scrollInfo.fMask = SIF_POS;
8599 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8601 /* Get real position value, the value we set might have been changed
8602 * by SetScrollInfo (especially if we went too far.
8604 scrollInfo.fMask = SIF_POS;
8605 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8606 if(uView == LVS_REPORT)
8608 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8611 /* only if the scroll position really changed, do we update screen */
8612 if (nOldScrollPos != scrollInfo.nPos)
8613 InvalidateRect(hwnd, NULL, TRUE);
8617 return 0;
8620 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8622 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8623 INT gcWheelDelta = 0;
8624 UINT pulScrollLines = 3;
8625 SCROLLINFO scrollInfo;
8627 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8629 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8630 gcWheelDelta -= wheelDelta;
8632 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8633 scrollInfo.cbSize = sizeof(SCROLLINFO);
8634 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8636 switch(uView)
8638 case LVS_ICON:
8639 case LVS_SMALLICON:
8641 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8642 * should be fixed in the future.
8644 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8645 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION,
8646 scrollInfo.nPos + (gcWheelDelta < 0) ?
8647 LISTVIEW_SCROLL_ICON_LINE_SIZE :
8648 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8649 break;
8651 case LVS_REPORT:
8652 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8654 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8656 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8657 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8658 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8661 break;
8663 case LVS_LIST:
8664 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8665 break;
8667 return 0;
8670 /***
8671 * DESCRIPTION:
8672 * ???
8674 * PARAMETER(S):
8675 * [I] HWND : window handle
8676 * [I] INT : virtual key
8677 * [I] LONG : key data
8679 * RETURN:
8680 * Zero
8682 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8684 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8685 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8686 INT nItem = -1;
8687 NMLVKEYDOWN nmKeyDown;
8689 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8691 /* send LVN_KEYDOWN notification */
8692 nmKeyDown.wVKey = nVirtualKey;
8693 nmKeyDown.flags = 0;
8694 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8696 switch (nVirtualKey)
8698 case VK_RETURN:
8699 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8701 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8702 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8704 break;
8706 case VK_HOME:
8707 if (GETITEMCOUNT(infoPtr) > 0)
8708 nItem = 0;
8709 break;
8711 case VK_END:
8712 if (GETITEMCOUNT(infoPtr) > 0)
8713 nItem = GETITEMCOUNT(infoPtr) - 1;
8714 break;
8716 case VK_LEFT:
8717 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8718 break;
8720 case VK_UP:
8721 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8722 break;
8724 case VK_RIGHT:
8725 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8726 break;
8728 case VK_DOWN:
8729 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8730 break;
8732 case VK_PRIOR:
8733 if (uView == LVS_REPORT)
8734 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8735 else
8736 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8737 * LISTVIEW_GetCountPerRow(hwnd);
8738 if(nItem < 0) nItem = 0;
8739 break;
8741 case VK_NEXT:
8742 if (uView == LVS_REPORT)
8743 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8744 else
8745 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8746 * LISTVIEW_GetCountPerRow(hwnd);
8747 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8748 break;
8751 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8753 if (LISTVIEW_KeySelection(hwnd, nItem))
8754 UpdateWindow(hwnd); /* update client area */
8757 return 0;
8760 /***
8761 * DESCRIPTION:
8762 * Kills the focus.
8764 * PARAMETER(S):
8765 * [I] HWND : window handle
8767 * RETURN:
8768 * Zero
8770 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8772 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8773 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8774 INT i,nTop,nBottom;
8776 TRACE("(hwnd=%x)\n", hwnd);
8778 /* send NM_KILLFOCUS notification */
8779 hdr_notify(hwnd, NM_KILLFOCUS);
8781 /* set window focus flag */
8782 infoPtr->bFocus = FALSE;
8784 /* NEED drawing optimization ; redraw the selected items */
8785 if (uView & LVS_REPORT)
8787 nTop = LISTVIEW_GetTopIndex(hwnd);
8788 nBottom = nTop +
8789 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8791 else
8793 nTop = 0;
8794 nBottom = GETITEMCOUNT(infoPtr);
8796 for (i = nTop; i<nBottom; i++)
8798 if (LISTVIEW_IsSelected(hwnd,i))
8800 RECT rcItem;
8801 rcItem.left = LVIR_BOUNDS;
8802 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8803 InvalidateRect(hwnd, &rcItem, FALSE);
8807 return 0;
8810 /***
8811 * DESCRIPTION:
8812 * Processes double click messages (left mouse button).
8814 * PARAMETER(S):
8815 * [I] HWND : window handle
8816 * [I] WORD : key flag
8817 * [I] WORD : x coordinate
8818 * [I] WORD : y coordinate
8820 * RETURN:
8821 * Zero
8823 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8824 WORD wPosY)
8826 LVHITTESTINFO htInfo;
8827 NMLISTVIEW nmlv;
8829 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8831 htInfo.pt.x = wPosX;
8832 htInfo.pt.y = wPosY;
8834 /* send NM_DBLCLK notification */
8835 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8836 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8838 nmlv.iItem = htInfo.iItem;
8839 nmlv.iSubItem = htInfo.iSubItem;
8841 else
8843 nmlv.iItem = -1;
8844 nmlv.iSubItem = 0;
8846 nmlv.ptAction.x = wPosX;
8847 nmlv.ptAction.y = wPosY;
8848 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8851 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8852 if(nmlv.iItem != -1)
8853 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8855 return 0;
8858 /***
8859 * DESCRIPTION:
8860 * Processes mouse down messages (left mouse button).
8862 * PARAMETER(S):
8863 * [I] HWND : window handle
8864 * [I] WORD : key flag
8865 * [I] WORD : x coordinate
8866 * [I] WORD : y coordinate
8868 * RETURN:
8869 * Zero
8871 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8872 WORD wPosY)
8874 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8875 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8876 static BOOL bGroupSelect = TRUE;
8877 POINT ptPosition;
8878 INT nItem;
8880 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8882 /* send NM_RELEASEDCAPTURE notification */
8883 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8885 if (infoPtr->bFocus == FALSE)
8886 SetFocus(hwnd);
8888 /* set left button down flag */
8889 infoPtr->bLButtonDown = TRUE;
8891 ptPosition.x = wPosX;
8892 ptPosition.y = wPosY;
8893 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8894 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8896 if (lStyle & LVS_SINGLESEL)
8898 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8899 && infoPtr->nEditLabelItem == -1)
8900 infoPtr->nEditLabelItem = nItem;
8901 else
8902 LISTVIEW_SetSelection(hwnd, nItem);
8904 else
8906 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8908 if (bGroupSelect)
8909 LISTVIEW_AddGroupSelection(hwnd, nItem);
8910 else
8911 LISTVIEW_AddSelection(hwnd, nItem);
8913 else if (wKey & MK_CONTROL)
8915 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8917 else if (wKey & MK_SHIFT)
8919 LISTVIEW_SetGroupSelection(hwnd, nItem);
8921 else
8923 BOOL was_selected =
8924 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8926 /* set selection (clears other pre-existing selections) */
8927 LISTVIEW_SetSelection(hwnd, nItem);
8929 if (was_selected && infoPtr->nEditLabelItem == -1)
8930 infoPtr->nEditLabelItem = nItem;
8934 else
8936 /* remove all selections */
8937 LISTVIEW_RemoveAllSelections(hwnd);
8940 /* redraw if we could have possibly selected something */
8941 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8943 return 0;
8946 /***
8947 * DESCRIPTION:
8948 * Processes mouse up messages (left mouse button).
8950 * PARAMETER(S):
8951 * [I] HWND : window handle
8952 * [I] WORD : key flag
8953 * [I] WORD : x coordinate
8954 * [I] WORD : y coordinate
8956 * RETURN:
8957 * Zero
8959 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8960 WORD wPosY)
8962 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8964 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8966 if (infoPtr->bLButtonDown != FALSE)
8968 LVHITTESTINFO lvHitTestInfo;
8969 NMLISTVIEW nmlv;
8971 lvHitTestInfo.pt.x = wPosX;
8972 lvHitTestInfo.pt.y = wPosY;
8974 /* send NM_CLICK notification */
8975 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8976 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8978 nmlv.iItem = lvHitTestInfo.iItem;
8979 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8981 else
8983 nmlv.iItem = -1;
8984 nmlv.iSubItem = 0;
8986 nmlv.ptAction.x = wPosX;
8987 nmlv.ptAction.y = wPosY;
8988 listview_notify(hwnd, NM_CLICK, &nmlv);
8990 /* set left button flag */
8991 infoPtr->bLButtonDown = FALSE;
8993 if(infoPtr->nEditLabelItem != -1)
8995 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL)
8996 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8997 infoPtr->nEditLabelItem = -1;
9001 return 0;
9004 /***
9005 * DESCRIPTION:
9006 * Creates the listview control (called before WM_CREATE).
9008 * PARAMETER(S):
9009 * [I] HWND : window handle
9010 * [I] WPARAM : unhandled
9011 * [I] LPARAM : widow creation info
9013 * RETURN:
9014 * Zero
9016 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
9018 LISTVIEW_INFO *infoPtr;
9020 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
9022 /* allocate memory for info structure */
9023 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
9024 if (infoPtr == NULL)
9026 ERR("could not allocate info memory!\n");
9027 return 0;
9030 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
9031 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
9033 ERR("pointer assignment error!\n");
9034 return 0;
9037 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
9040 /***
9041 * DESCRIPTION:
9042 * Destroys the listview control (called after WM_DESTROY).
9044 * PARAMETER(S):
9045 * [I] HWND : window handle
9047 * RETURN:
9048 * Zero
9050 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
9052 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9053 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9055 TRACE("(hwnd=%x)\n", hwnd);
9057 /* delete all items */
9058 LISTVIEW_DeleteAllItems(hwnd);
9060 /* destroy data structure */
9061 DPA_Destroy(infoPtr->hdpaItems);
9062 DPA_Destroy(infoPtr->hdpaSelectionRanges);
9064 /* destroy image lists */
9065 if (!(lStyle & LVS_SHAREIMAGELISTS))
9067 #if 0
9068 /* FIXME: If the caller does a ImageList_Destroy and then we
9069 * do this code the area will be freed twice. Currently
9070 * this generates an "err:heap:HEAP_ValidateInUseArena
9071 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
9072 * has PREV_FREE flag" sometimes.
9074 * We will leak the memory till we figure out how to fix
9076 if (infoPtr->himlNormal)
9077 ImageList_Destroy(infoPtr->himlNormal);
9078 if (infoPtr->himlSmall)
9079 ImageList_Destroy(infoPtr->himlSmall);
9080 if (infoPtr->himlState)
9081 ImageList_Destroy(infoPtr->himlState);
9082 #endif
9085 /* destroy font */
9086 infoPtr->hFont = (HFONT)0;
9087 if (infoPtr->hDefaultFont)
9089 DeleteObject(infoPtr->hDefaultFont);
9092 /* free listview info pointer*/
9093 COMCTL32_Free(infoPtr);
9095 SetWindowLongW(hwnd, 0, 0);
9096 return 0;
9099 /***
9100 * DESCRIPTION:
9101 * Handles notifications from children.
9103 * PARAMETER(S):
9104 * [I] HWND : window handle
9105 * [I] INT : control identifier
9106 * [I] LPNMHDR : notification information
9108 * RETURN:
9109 * Zero
9111 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
9113 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9115 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
9117 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
9119 /* handle notification from header control */
9120 if (lpnmh->code == HDN_ENDTRACKW)
9122 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9123 InvalidateRect(hwnd, NULL, TRUE);
9125 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
9127 /* Handle sorting by Header Column */
9128 NMLISTVIEW nmlv;
9130 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9131 nmlv.iItem = -1;
9132 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
9133 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
9135 else if(lpnmh->code == NM_RELEASEDCAPTURE)
9137 /* Idealy this should be done in HDN_ENDTRACKA
9138 * but since SetItemBounds in Header.c is called after
9139 * the notification is sent, it is neccessary to handle the
9140 * update of the scroll bar here (Header.c works fine as it is,
9141 * no need to disturb it)
9143 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9144 LISTVIEW_UpdateScroll(hwnd);
9145 InvalidateRect(hwnd, NULL, TRUE);
9150 return 0;
9153 /***
9154 * DESCRIPTION:
9155 * Determines the type of structure to use.
9157 * PARAMETER(S):
9158 * [I] HWND : window handle of the sender
9159 * [I] HWND : listview window handle
9160 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
9162 * RETURN:
9163 * Zero
9165 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
9167 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9169 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
9171 if (nCommand == NF_REQUERY)
9172 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
9173 (WPARAM)hwnd, (LPARAM)NF_QUERY);
9174 return 0;
9177 /***
9178 * DESCRIPTION:
9179 * Paints/Repaints the listview control.
9181 * PARAMETER(S):
9182 * [I] HWND : window handle
9183 * [I] HDC : device context handle
9185 * RETURN:
9186 * Zero
9188 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
9190 PAINTSTRUCT ps;
9192 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
9194 if (hdc == 0)
9196 hdc = BeginPaint(hwnd, &ps);
9197 LISTVIEW_Refresh(hwnd, hdc);
9198 EndPaint(hwnd, &ps);
9200 else
9202 LISTVIEW_Refresh(hwnd, hdc);
9205 return 0;
9208 /***
9209 * DESCRIPTION:
9210 * Processes double click messages (right mouse button).
9212 * PARAMETER(S):
9213 * [I] HWND : window handle
9214 * [I] WORD : key flag
9215 * [I] WORD : x coordinate
9216 * [I] WORD : y coordinate
9218 * RETURN:
9219 * Zero
9221 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
9222 WORD wPosY)
9224 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9226 /* send NM_RELEASEDCAPTURE notification */
9227 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
9229 /* send NM_RDBLCLK notification */
9230 hdr_notify(hwnd, NM_RDBLCLK);
9232 return 0;
9235 /***
9236 * DESCRIPTION:
9237 * Processes mouse down messages (right mouse button).
9239 * PARAMETER(S):
9240 * [I] HWND : window handle
9241 * [I] WORD : key flag
9242 * [I] WORD : x coordinate
9243 * [I] WORD : y coordinate
9245 * RETURN:
9246 * Zero
9248 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
9249 WORD wPosY)
9251 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9252 POINT ptPosition;
9253 INT nItem;
9254 NMLISTVIEW nmlv;
9255 LVHITTESTINFO lvHitTestInfo;
9257 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9259 /* send NM_RELEASEDCAPTURE notification */
9260 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
9262 /* make sure the listview control window has the focus */
9263 if (infoPtr->bFocus == FALSE)
9264 SetFocus(hwnd);
9266 /* set right button down flag */
9267 infoPtr->bRButtonDown = TRUE;
9269 /* determine the index of the selected item */
9270 ptPosition.x = wPosX;
9271 ptPosition.y = wPosY;
9272 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
9273 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
9275 LISTVIEW_SetItemFocus(hwnd,nItem);
9276 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9277 !LISTVIEW_IsSelected(hwnd,nItem))
9278 LISTVIEW_SetSelection(hwnd, nItem);
9280 else
9282 LISTVIEW_RemoveAllSelections(hwnd);
9285 lvHitTestInfo.pt.x = wPosX;
9286 lvHitTestInfo.pt.y = wPosY;
9288 /* Send NM_RClICK notification */
9289 ZeroMemory(&nmlv, sizeof(nmlv));
9290 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
9292 nmlv.iItem = lvHitTestInfo.iItem;
9293 nmlv.iSubItem = lvHitTestInfo.iSubItem;
9295 else
9297 nmlv.iItem = -1;
9298 nmlv.iSubItem = 0;
9300 nmlv.ptAction.x = wPosX;
9301 nmlv.ptAction.y = wPosY;
9302 listview_notify(hwnd, NM_RCLICK, &nmlv);
9304 return 0;
9307 /***
9308 * DESCRIPTION:
9309 * Processes mouse up messages (right mouse button).
9311 * PARAMETER(S):
9312 * [I] HWND : window handle
9313 * [I] WORD : key flag
9314 * [I] WORD : x coordinate
9315 * [I] WORD : y coordinate
9317 * RETURN:
9318 * Zero
9320 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
9321 WORD wPosY)
9323 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9325 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9327 if (infoPtr->bRButtonDown)
9329 POINT pt;
9331 pt.x = wPosX;
9332 pt.y = wPosY;
9334 /* set button flag */
9335 infoPtr->bRButtonDown = FALSE;
9337 /* Change to screen coordinate for WM_CONTEXTMENU */
9338 ClientToScreen(hwnd, &pt);
9340 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9341 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9344 return 0;
9347 /***
9348 * DESCRIPTION:
9349 * Sets the focus.
9351 * PARAMETER(S):
9352 * [I] HWND : window handle
9353 * [I] HWND : window handle of previously focused window
9355 * RETURN:
9356 * Zero
9358 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9360 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9362 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9364 /* send NM_SETFOCUS notification */
9365 hdr_notify(hwnd, NM_SETFOCUS);
9367 /* set window focus flag */
9368 infoPtr->bFocus = TRUE;
9370 UpdateWindow(hwnd);
9372 return 0;
9375 /***
9376 * DESCRIPTION:
9377 * Sets the font.
9379 * PARAMETER(S):
9380 * [I] HWND : window handle
9381 * [I] HFONT : font handle
9382 * [I] WORD : redraw flag
9384 * RETURN:
9385 * Zero
9387 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9389 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9390 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9392 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9394 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9395 LISTVIEW_SaveTextMetrics(hwnd);
9397 if (uView == LVS_REPORT)
9399 /* set header font */
9400 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9401 MAKELPARAM(fRedraw, 0));
9404 /* invalidate listview control client area */
9405 InvalidateRect(hwnd, NULL, TRUE);
9407 if (fRedraw != FALSE)
9408 UpdateWindow(hwnd);
9410 return 0;
9413 /***
9414 * DESCRIPTION:
9415 * Message handling for WM_SETREDRAW.
9416 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9418 * PARAMETER(S):
9419 * [I] HWND : window handle
9420 * [I] bRedraw: state of redraw flag
9422 * RETURN:
9423 * DefWinProc return value
9425 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9427 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9428 if(bRedraw)
9429 RedrawWindow(hwnd, NULL, 0,
9430 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9431 return lResult;
9434 /***
9435 * DESCRIPTION:
9436 * Resizes the listview control. This function processes WM_SIZE
9437 * messages. At this time, the width and height are not used.
9439 * PARAMETER(S):
9440 * [I] HWND : window handle
9441 * [I] WORD : new width
9442 * [I] WORD : new height
9444 * RETURN:
9445 * Zero
9447 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9449 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9450 UINT uView = lStyle & LVS_TYPEMASK;
9452 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9454 if (LISTVIEW_UpdateSize(hwnd))
9456 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9458 if (lStyle & LVS_ALIGNLEFT)
9459 LISTVIEW_AlignLeft(hwnd);
9460 else
9461 LISTVIEW_AlignTop(hwnd);
9464 LISTVIEW_UpdateScroll(hwnd);
9466 /* invalidate client area + erase background */
9467 InvalidateRect(hwnd, NULL, TRUE);
9470 return 0;
9473 /***
9474 * DESCRIPTION:
9475 * Sets the size information.
9477 * PARAMETER(S):
9478 * [I] HWND : window handle
9480 * RETURN:
9481 * Zero if no size change
9482 * 1 of size changed
9484 static BOOL LISTVIEW_UpdateSize(HWND hwnd)
9486 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9487 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9488 UINT uView = lStyle & LVS_TYPEMASK;
9489 RECT rcList;
9490 RECT rcOld;
9492 TRACE("(hwnd=%x)\n", hwnd);
9494 GetClientRect(hwnd, &rcList);
9495 CopyRect(&rcOld,&(infoPtr->rcList));
9496 infoPtr->rcList.left = 0;
9497 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9498 infoPtr->rcList.top = 0;
9499 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9501 if (uView == LVS_LIST)
9503 /* Apparently the "LIST" style is supposed to have the same
9504 * number of items in a column even if there is no scroll bar.
9505 * Since if a scroll bar already exists then the bottom is already
9506 * reduced, only reduce if the scroll bar does not currently exist.
9507 * The "2" is there to mimic the native control. I think it may be
9508 * related to either padding or edges. (GLA 7/2002)
9510 if (!(lStyle & WS_HSCROLL))
9512 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9513 if (infoPtr->rcList.bottom > nHScrollHeight)
9514 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
9516 else
9518 if (infoPtr->rcList.bottom > 2)
9519 infoPtr->rcList.bottom -= 2;
9522 else if (uView == LVS_REPORT)
9524 HDLAYOUT hl;
9525 WINDOWPOS wp;
9527 hl.prc = &rcList;
9528 hl.pwpos = &wp;
9529 Header_Layout(infoPtr->hwndHeader, &hl);
9531 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9533 if (!(LVS_NOCOLUMNHEADER & lStyle))
9534 infoPtr->rcList.top = max(wp.cy, 0);
9536 return (EqualRect(&rcOld,&(infoPtr->rcList)));
9539 /***
9540 * DESCRIPTION:
9541 * Processes WM_STYLECHANGED messages.
9543 * PARAMETER(S):
9544 * [I] HWND : window handle
9545 * [I] WPARAM : window style type (normal or extended)
9546 * [I] LPSTYLESTRUCT : window style information
9548 * RETURN:
9549 * Zero
9551 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9552 LPSTYLESTRUCT lpss)
9554 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9555 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9556 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9557 RECT rcList = infoPtr->rcList;
9559 TRACE("(hwnd=%x, styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
9560 hwnd, wStyleType, lpss->styleOld, lpss->styleNew);
9562 if (wStyleType == GWL_STYLE)
9564 if (uOldView == LVS_REPORT)
9565 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9567 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9568 ((lpss->styleNew & WS_HSCROLL) == 0))
9569 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9571 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9572 ((lpss->styleNew & WS_VSCROLL) == 0))
9573 ShowScrollBar(hwnd, SB_VERT, FALSE);
9575 /* If switching modes, then start with no scroll bars and then
9576 * decide.
9578 if (uNewView != uOldView)
9579 ShowScrollBar(hwnd, SB_BOTH, FALSE);
9581 if (uNewView == LVS_ICON)
9583 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9584 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9585 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9586 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9587 if (lpss->styleNew & LVS_ALIGNLEFT)
9588 LISTVIEW_AlignLeft(hwnd);
9589 else
9590 LISTVIEW_AlignTop(hwnd);
9592 else if (uNewView == LVS_REPORT)
9594 HDLAYOUT hl;
9595 WINDOWPOS wp;
9597 hl.prc = &rcList;
9598 hl.pwpos = &wp;
9599 Header_Layout(infoPtr->hwndHeader, &hl);
9600 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9601 wp.flags);
9602 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9603 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9605 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9606 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9607 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9608 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9610 else if (uNewView == LVS_LIST)
9612 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9613 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9614 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9615 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9617 else
9619 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9620 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9621 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9622 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9623 if (lpss->styleNew & LVS_ALIGNLEFT)
9624 LISTVIEW_AlignLeft(hwnd);
9625 else
9626 LISTVIEW_AlignTop(hwnd);
9629 /* update the size of the client area */
9630 LISTVIEW_UpdateSize(hwnd);
9632 /* add scrollbars if needed */
9633 LISTVIEW_UpdateScroll(hwnd);
9635 /* invalidate client area + erase background */
9636 InvalidateRect(hwnd, NULL, TRUE);
9638 /* print the list of unsupported window styles */
9639 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9642 /* If they change the view and we have an active edit control
9643 we will need to kill the control since the redraw will
9644 misplace the edit control.
9646 if (infoPtr->hwndEdit &&
9647 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9648 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9650 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9653 return 0;
9656 /***
9657 * DESCRIPTION:
9658 * Window procedure of the listview control.
9661 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9662 LPARAM lParam)
9664 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9665 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9666 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9667 switch (uMsg)
9669 case LVM_APPROXIMATEVIEWRECT:
9670 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9671 LOWORD(lParam), HIWORD(lParam));
9672 case LVM_ARRANGE:
9673 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9675 /* case LVM_CREATEDRAGIMAGE: */
9677 case LVM_DELETEALLITEMS:
9678 return LISTVIEW_DeleteAllItems(hwnd);
9680 case LVM_DELETECOLUMN:
9681 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9683 case LVM_DELETEITEM:
9684 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9686 case LVM_EDITLABELW:
9687 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9689 case LVM_EDITLABELA:
9690 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9692 case LVM_ENSUREVISIBLE:
9693 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9695 case LVM_FINDITEMW:
9696 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9698 case LVM_FINDITEMA:
9699 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9701 case LVM_GETBKCOLOR:
9702 return LISTVIEW_GetBkColor(hwnd);
9704 /* case LVM_GETBKIMAGE: */
9706 case LVM_GETCALLBACKMASK:
9707 return LISTVIEW_GetCallbackMask(hwnd);
9709 case LVM_GETCOLUMNA:
9710 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9712 case LVM_GETCOLUMNW:
9713 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9715 case LVM_GETCOLUMNORDERARRAY:
9716 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9718 case LVM_GETCOLUMNWIDTH:
9719 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9721 case LVM_GETCOUNTPERPAGE:
9722 return LISTVIEW_GetCountPerPage(hwnd);
9724 case LVM_GETEDITCONTROL:
9725 return LISTVIEW_GetEditControl(hwnd);
9727 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9728 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9730 case LVM_GETHEADER:
9731 return LISTVIEW_GetHeader(hwnd);
9733 case LVM_GETHOTCURSOR:
9734 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9735 return FALSE;
9737 case LVM_GETHOTITEM:
9738 return LISTVIEW_GetHotItem(hwnd);
9740 case LVM_GETHOVERTIME:
9741 return LISTVIEW_GetHoverTime(hwnd);
9743 case LVM_GETIMAGELIST:
9744 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9746 case LVM_GETISEARCHSTRINGA:
9747 case LVM_GETISEARCHSTRINGW:
9748 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9749 return FALSE;
9751 case LVM_GETITEMA:
9752 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9754 case LVM_GETITEMW:
9755 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9757 case LVM_GETITEMCOUNT:
9758 return LISTVIEW_GetItemCount(hwnd);
9760 case LVM_GETITEMPOSITION:
9761 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9763 case LVM_GETITEMRECT:
9764 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9766 case LVM_GETITEMSPACING:
9767 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9769 case LVM_GETITEMSTATE:
9770 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9772 case LVM_GETITEMTEXTA:
9773 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9775 case LVM_GETITEMTEXTW:
9776 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9778 case LVM_GETNEXTITEM:
9779 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9781 case LVM_GETNUMBEROFWORKAREAS:
9782 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9783 return 1;
9785 case LVM_GETORIGIN:
9786 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9788 case LVM_GETSELECTEDCOUNT:
9789 return LISTVIEW_GetSelectedCount(hwnd);
9791 case LVM_GETSELECTIONMARK:
9792 return LISTVIEW_GetSelectionMark(hwnd);
9794 case LVM_GETSTRINGWIDTHA:
9795 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9797 case LVM_GETSTRINGWIDTHW:
9798 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9800 case LVM_GETSUBITEMRECT:
9801 return LISTVIEW_GetSubItemRect(hwnd, (UINT)wParam, ((LPRECT)lParam)->top,
9802 ((LPRECT)lParam)->left, (LPRECT)lParam);
9804 case LVM_GETTEXTBKCOLOR:
9805 return LISTVIEW_GetTextBkColor(hwnd);
9807 case LVM_GETTEXTCOLOR:
9808 return LISTVIEW_GetTextColor(hwnd);
9810 case LVM_GETTOOLTIPS:
9811 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9812 return FALSE;
9814 case LVM_GETTOPINDEX:
9815 return LISTVIEW_GetTopIndex(hwnd);
9817 /*case LVM_GETUNICODEFORMAT:
9818 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9819 return FALSE;*/
9821 case LVM_GETVIEWRECT:
9822 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9824 case LVM_GETWORKAREAS:
9825 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9826 return FALSE;
9828 case LVM_HITTEST:
9829 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9831 case LVM_INSERTCOLUMNA:
9832 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9834 case LVM_INSERTCOLUMNW:
9835 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9837 case LVM_INSERTITEMA:
9838 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9840 case LVM_INSERTITEMW:
9841 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9843 case LVM_REDRAWITEMS:
9844 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9846 case LVM_SCROLL:
9847 return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam);
9849 case LVM_SETBKCOLOR:
9850 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9852 /* case LVM_SETBKIMAGE: */
9854 case LVM_SETCALLBACKMASK:
9855 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9857 case LVM_SETCOLUMNA:
9858 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9860 case LVM_SETCOLUMNW:
9861 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9863 case LVM_SETCOLUMNORDERARRAY:
9864 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9866 case LVM_SETCOLUMNWIDTH:
9867 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9869 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9870 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9872 /* case LVM_SETHOTCURSOR: */
9874 case LVM_SETHOTITEM:
9875 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9877 case LVM_SETHOVERTIME:
9878 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9880 case LVM_SETICONSPACING:
9881 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9883 case LVM_SETIMAGELIST:
9884 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9886 case LVM_SETITEMA:
9887 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9889 case LVM_SETITEMW:
9890 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9892 case LVM_SETITEMCOUNT:
9893 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9895 case LVM_SETITEMPOSITION:
9896 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9897 (INT)HIWORD(lParam));
9899 case LVM_SETITEMPOSITION32:
9900 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9901 ((POINT*)lParam)->y);
9903 case LVM_SETITEMSTATE:
9904 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9906 case LVM_SETITEMTEXTA:
9907 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9909 case LVM_SETITEMTEXTW:
9910 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9912 case LVM_SETSELECTIONMARK:
9913 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9915 case LVM_SETTEXTBKCOLOR:
9916 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9918 case LVM_SETTEXTCOLOR:
9919 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9921 /* case LVM_SETTOOLTIPS: */
9922 /* case LVM_SETUNICODEFORMAT: */
9923 /* case LVM_SETWORKAREAS: */
9925 case LVM_SORTITEMS:
9926 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9928 case LVM_SUBITEMHITTEST:
9929 return LISTVIEW_SubItemHitTest(hwnd, (LPLVHITTESTINFO)lParam);
9931 case LVM_UPDATE:
9932 return LISTVIEW_Update(hwnd, (INT)wParam);
9934 case WM_CHAR:
9935 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9937 case WM_COMMAND:
9938 return LISTVIEW_Command(hwnd, wParam, lParam);
9940 case WM_CREATE:
9941 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9943 case WM_ERASEBKGND:
9944 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9946 case WM_GETDLGCODE:
9947 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9949 case WM_GETFONT:
9950 return LISTVIEW_GetFont(hwnd);
9952 case WM_HSCROLL:
9953 if (SLOWORD(wParam) < 0) return 0; /* validate not internal codes */
9954 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9955 (INT)HIWORD(wParam), (HWND)lParam);
9957 case WM_KEYDOWN:
9958 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9960 case WM_KILLFOCUS:
9961 return LISTVIEW_KillFocus(hwnd);
9963 case WM_LBUTTONDBLCLK:
9964 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9965 HIWORD(lParam));
9967 case WM_LBUTTONDOWN:
9968 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9969 HIWORD(lParam));
9970 case WM_LBUTTONUP:
9971 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9972 HIWORD(lParam));
9973 case WM_MOUSEMOVE:
9974 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9976 case WM_MOUSEHOVER:
9977 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9979 case WM_NCCREATE:
9980 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9982 case WM_NCDESTROY:
9983 return LISTVIEW_NCDestroy(hwnd);
9985 case WM_NOTIFY:
9986 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9988 case WM_NOTIFYFORMAT:
9989 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9991 case WM_PAINT:
9992 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9994 case WM_RBUTTONDBLCLK:
9995 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9996 HIWORD(lParam));
9998 case WM_RBUTTONDOWN:
9999 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
10000 HIWORD(lParam));
10002 case WM_RBUTTONUP:
10003 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
10004 HIWORD(lParam));
10006 case WM_SETFOCUS:
10007 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
10009 case WM_SETFONT:
10010 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
10012 case WM_SETREDRAW:
10013 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
10015 case WM_SIZE:
10016 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
10018 case WM_STYLECHANGED:
10019 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
10021 case WM_SYSCOLORCHANGE:
10022 COMCTL32_RefreshSysColors();
10023 return 0;
10025 /* case WM_TIMER: */
10027 case WM_VSCROLL:
10028 if (SLOWORD(wParam) < 0) return 0; /* validate not internal codes */
10029 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
10030 (INT)HIWORD(wParam), (HWND)lParam);
10032 case WM_MOUSEWHEEL:
10033 if (wParam & (MK_SHIFT | MK_CONTROL))
10034 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
10035 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));
10037 case WM_WINDOWPOSCHANGED:
10038 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
10039 SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10040 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10041 LISTVIEW_UpdateSize(hwnd);
10042 LISTVIEW_UpdateScroll(hwnd);
10044 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10046 /* case WM_WININICHANGE: */
10048 default:
10049 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
10051 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
10052 lParam);
10055 /* call default window procedure */
10056 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10059 return 0;
10062 /***
10063 * DESCRIPTION:
10064 * Registers the window class.
10066 * PARAMETER(S):
10067 * None
10069 * RETURN:
10070 * None
10072 VOID LISTVIEW_Register(void)
10074 WNDCLASSW wndClass;
10076 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10077 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10078 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
10079 wndClass.cbClsExtra = 0;
10080 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10081 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
10082 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10083 wndClass.lpszClassName = WC_LISTVIEWW;
10084 RegisterClassW(&wndClass);
10087 /***
10088 * DESCRIPTION:
10089 * Unregisters the window class.
10091 * PARAMETER(S):
10092 * None
10094 * RETURN:
10095 * None
10097 VOID LISTVIEW_Unregister(void)
10099 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
10102 /***
10103 * DESCRIPTION:
10104 * Handle any WM_COMMAND messages
10106 * PARAMETER(S):
10108 * RETURN:
10110 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
10112 switch (HIWORD(wParam))
10114 case EN_UPDATE:
10117 * Adjust the edit window size
10119 WCHAR buffer[1024];
10120 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
10121 HDC hdc = GetDC(infoPtr->hwndEdit);
10122 HFONT hFont, hOldFont = 0;
10123 RECT rect;
10124 SIZE sz;
10125 int len;
10127 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10128 GetWindowRect(infoPtr->hwndEdit, &rect);
10130 /* Select font to get the right dimension of the string */
10131 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10132 if(hFont != 0)
10134 hOldFont = SelectObject(hdc, hFont);
10137 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10139 TEXTMETRICW textMetric;
10141 /* Add Extra spacing for the next character */
10142 GetTextMetricsW(hdc, &textMetric);
10143 sz.cx += (textMetric.tmMaxCharWidth * 2);
10145 SetWindowPos (
10146 infoPtr->hwndEdit,
10147 HWND_TOP,
10150 sz.cx,
10151 rect.bottom - rect.top,
10152 SWP_DRAWFRAME|SWP_NOMOVE);
10154 if(hFont != 0)
10155 SelectObject(hdc, hOldFont);
10157 ReleaseDC(hwnd, hdc);
10159 break;
10162 default:
10163 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
10166 return 0;
10170 /***
10171 * DESCRIPTION:
10172 * Subclassed edit control windproc function
10174 * PARAMETER(S):
10176 * RETURN:
10178 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
10179 WPARAM wParam, LPARAM lParam, BOOL isW)
10181 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
10182 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
10183 static BOOL bIgnoreKillFocus = FALSE;
10184 BOOL cancel = FALSE;
10186 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
10187 hwnd, uMsg, wParam, lParam, isW);
10189 switch (uMsg)
10191 case WM_GETDLGCODE:
10192 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10194 case WM_KILLFOCUS:
10195 if(bIgnoreKillFocus) return TRUE;
10196 break;
10198 case WM_DESTROY:
10200 WNDPROC editProc = einfo->EditWndProc;
10201 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
10202 COMCTL32_Free(einfo);
10203 infoPtr->pedititem = NULL;
10204 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10207 case WM_KEYDOWN:
10208 if (VK_ESCAPE == (INT)wParam)
10210 cancel = TRUE;
10211 break;
10213 else if (VK_RETURN == (INT)wParam)
10214 break;
10216 default:
10217 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10220 if (einfo->EditLblCb)
10222 LPWSTR buffer = NULL;
10224 if (!cancel)
10226 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10228 if (len)
10230 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10232 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10233 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10237 /* Processing LVN_ENDLABELEDIT message could kill the focus */
10238 /* eg. Using a messagebox */
10239 bIgnoreKillFocus = TRUE;
10240 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
10242 if (buffer) COMCTL32_Free(buffer);
10244 einfo->EditLblCb = NULL;
10245 bIgnoreKillFocus = FALSE;
10248 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10249 return TRUE;
10252 /***
10253 * DESCRIPTION:
10254 * Subclassed edit control windproc function
10256 * PARAMETER(S):
10258 * RETURN:
10260 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10262 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10265 /***
10266 * DESCRIPTION:
10267 * Subclassed edit control windproc function
10269 * PARAMETER(S):
10271 * RETURN:
10273 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10275 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10278 /***
10279 * DESCRIPTION:
10280 * Creates a subclassed edit cotrol
10282 * PARAMETER(S):
10284 * RETURN:
10286 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
10287 INT width, INT height, HWND parent, HINSTANCE hinst,
10288 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
10290 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
10291 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10292 HWND hedit;
10293 SIZE sz;
10294 HDC hdc;
10295 HDC hOldFont=0;
10296 TEXTMETRICW textMetric;
10298 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
10300 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
10301 return 0;
10303 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
10304 hdc = GetDC(parent);
10306 /* Select the font to get appropriate metric dimensions */
10307 if(infoPtr->hFont != 0)
10308 hOldFont = SelectObject(hdc, infoPtr->hFont);
10310 /*Get String Lenght in pixels */
10311 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10313 /*Add Extra spacing for the next character */
10314 GetTextMetricsW(hdc, &textMetric);
10315 sz.cx += (textMetric.tmMaxCharWidth * 2);
10317 if(infoPtr->hFont != 0)
10318 SelectObject(hdc, hOldFont);
10320 ReleaseDC(parent, hdc);
10321 if (isW)
10322 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10323 else
10324 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10326 if (!hedit)
10328 COMCTL32_Free(infoPtr->pedititem);
10329 return 0;
10332 infoPtr->pedititem->param = param;
10333 infoPtr->pedititem->EditLblCb = EditLblCb;
10334 infoPtr->pedititem->EditWndProc = (WNDPROC)
10335 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
10336 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
10338 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
10340 return hedit;