Listview_ProcessLetterKeys: fixed never ending loop when end index is
[wine.git] / dlls / comctl32 / listview.c
blob28771830d24ad7e0bc146c8f3e83e54954520da5
1 /*
2 * Listview control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 Codeweavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * NOTES
25 * Listview control implementation.
27 * TODO:
28 * 1. No horizontal scrolling when header is larger than the client area.
29 * 2. Drawing optimizations.
30 * 3. Hot item handling.
32 * Notifications:
33 * LISTVIEW_Notify : most notifications from children (editbox and header)
35 * Data structure:
36 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
38 * Advanced functionality:
39 * LISTVIEW_GetNumberOfWorkAreas : not implemented
40 * LISTVIEW_GetISearchString : not implemented
41 * LISTVIEW_GetBkImage : not implemented
42 * LISTVIEW_SetBkImage : not implemented
43 * LISTVIEW_GetColumnOrderArray : simple hack only
44 * LISTVIEW_SetColumnOrderArray : simple hack only
45 * LISTVIEW_Arrange : empty stub
46 * LISTVIEW_ApproximateViewRect : incomplete
47 * LISTVIEW_Update : not completed
49 * Known differences in message stream from native control (not known if
50 * these differences cause problems):
51 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
52 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
53 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
54 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
55 * does *not* invoke DefWindowProc
56 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
57 * processing for "USEDOUBLECLICKTIME".
61 #include "config.h"
62 #include "wine/port.h"
64 #include <ctype.h>
65 #include <string.h>
66 #include <stdlib.h>
67 #include <stdio.h>
69 #include "winbase.h"
70 #include "winnt.h"
71 #include "heap.h"
72 #include "commctrl.h"
73 #include "comctl32.h"
74 #include "wine/debug.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(listview);
78 /* Some definitions for inline edit control */
80 typedef struct tagLV_INTHIT
82 LVHITTESTINFO ht;
83 DWORD distance; /* distance to closest item */
84 INT iDistItem; /* item number that is closest */
85 } LV_INTHIT, *LPLV_INTHIT;
87 typedef struct tagITEMHDR
89 LPWSTR pszText;
90 INT iImage;
91 } ITEMHDR, *LPITEMHDR;
93 typedef struct tagLISTVIEW_SUBITEM
95 ITEMHDR hdr;
96 INT iSubItem;
97 } LISTVIEW_SUBITEM;
99 typedef struct tagLISTVIEW_ITEM
101 ITEMHDR hdr;
102 UINT state;
103 LPARAM lParam;
104 INT iIndent;
105 POINT ptPosition;
106 BOOL valid;
107 } LISTVIEW_ITEM;
109 typedef struct tagLISTVIEW_SELECTION
111 DWORD lower;
112 DWORD upper;
113 } LISTVIEW_SELECTION;
115 typedef struct tagLISTVIEW_INFO
117 HWND hwndSelf;
118 HBRUSH hBkBrush;
119 COLORREF clrBk;
120 COLORREF clrText;
121 COLORREF clrTextBk;
122 HIMAGELIST himlNormal;
123 HIMAGELIST himlSmall;
124 HIMAGELIST himlState;
125 BOOL bLButtonDown;
126 BOOL bRButtonDown;
127 INT nItemHeight;
128 INT nItemWidth;
129 HDPA hdpaSelectionRanges;
130 INT nSelectionMark;
131 INT nHotItem;
132 SHORT notifyFormat;
133 RECT rcList;
134 RECT rcView;
135 SIZE iconSize;
136 SIZE iconSpacing;
137 UINT uCallbackMask;
138 HWND hwndHeader;
139 HFONT hDefaultFont;
140 HCURSOR hHotCursor;
141 HFONT hFont;
142 INT ntmHeight; /* from GetTextMetrics from above font */
143 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
144 BOOL bFocus;
145 INT nFocusedItem;
146 RECT rcFocus;
147 DWORD dwExStyle; /* extended listview style */
148 HDPA hdpaItems;
149 PFNLVCOMPARE pfnCompare;
150 LPARAM lParamSort;
151 HWND hwndEdit;
152 BOOL bEditing;
153 WNDPROC EditWndProc;
154 INT nEditLabelItem;
155 DWORD dwHoverTime;
156 INT nColumnCount; /* the number of columns in this control */
158 DWORD lastKeyPressTimestamp; /* Added */
159 WPARAM charCode; /* Added */
160 INT nSearchParamLength; /* Added */
161 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
162 BOOL bIsDrawing;
163 } LISTVIEW_INFO;
165 DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO, hwndSelf);
168 * constants
171 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
172 #define SB_INTERNAL -1
174 /* maximum size of a label */
175 #define DISP_TEXT_SIZE 512
177 /* padding for items in list and small icon display modes */
178 #define WIDTH_PADDING 12
180 /* padding for items in list, report and small icon display modes */
181 #define HEIGHT_PADDING 1
183 /* offset of items in report display mode */
184 #define REPORT_MARGINX 2
186 /* padding for icon in large icon display mode
187 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
188 * that HITTEST will see.
189 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
190 * ICON_TOP_PADDING - sum of the two above.
191 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
192 * LABEL_VERT_PADDING - between bottom of text and end of box
194 #define ICON_TOP_PADDING_NOTHITABLE 2
195 #define ICON_TOP_PADDING_HITABLE 2
196 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
197 #define ICON_BOTTOM_PADDING 4
198 #define LABEL_VERT_PADDING 7
200 /* default label width for items in list and small icon display modes */
201 #define DEFAULT_LABEL_WIDTH 40
203 /* default column width for items in list display mode */
204 #define DEFAULT_COLUMN_WIDTH 128
206 /* Size of "line" scroll for V & H scrolls */
207 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
209 /* Padding betwen image and label */
210 #define IMAGE_PADDING 2
212 /* Padding behind the label */
213 #define TRAILING_PADDING 5
215 /* Border for the icon caption */
216 #define CAPTION_BORDER 2
218 /* Standard DrawText flags for LISTVIEW_UpdateLargeItemLabelRect and LISTVIEW_DrawLargeItem */
219 #define LISTVIEW_DTFLAGS DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL
222 * macros
224 /* retrieve the number of items in the listview */
225 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
227 #define LISTVIEW_DUMP(iP) do { \
228 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
229 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
230 iP->nItemHeight, iP->nItemWidth, GetWindowLongW (iP->hwndSelf, GWL_STYLE)); \
231 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n", \
232 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
233 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle); \
234 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
235 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
236 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
237 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
238 iP->hwndSelf, \
239 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
240 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
241 } while(0)
245 * forward declarations
247 static LRESULT LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL, BOOL);
248 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *, LPLV_INTHIT, BOOL);
249 static INT LISTVIEW_HitTestItem(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL);
250 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
251 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
252 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
253 static void LISTVIEW_AddSelection(LISTVIEW_INFO *, INT);
254 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *);
255 static BOOL LISTVIEW_GetItemBoundBox(LISTVIEW_INFO *, INT, LPRECT);
256 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
257 static LRESULT LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
258 static LRESULT LISTVIEW_GetSubItemRect(LISTVIEW_INFO *, INT, INT, INT, LPRECT);
259 static INT LISTVIEW_GetItemWidth(LISTVIEW_INFO *);
260 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
261 static LRESULT LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
262 static LRESULT LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
263 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
264 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
265 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
266 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
267 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, LONG, LONG);
268 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
269 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
270 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
271 static LRESULT LISTVIEW_SetViewRect(LISTVIEW_INFO *, LPRECT);
272 static void LISTVIEW_UnsupportedStyles(LONG);
273 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
274 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
275 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
276 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
277 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *, WPARAM, LPARAM);
278 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
279 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
280 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
281 static void LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *, int, RECT*);
282 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
283 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
284 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
285 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
286 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
287 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
289 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
290 #define KEY_DELAY 450
292 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
294 static inline BOOL is_textW(LPCWSTR text)
296 return text != NULL && text != LPSTR_TEXTCALLBACKW;
299 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
301 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
302 return is_textW(text);
305 static inline int textlenT(LPCWSTR text, BOOL isW)
307 return !is_textT(text, isW) ? 0 :
308 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
311 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
313 if (isDestW)
314 if (isSrcW) lstrcpynW(dest, src, max);
315 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
316 else
317 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
318 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
321 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
323 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
324 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
327 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
329 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
330 n = min(textlenT(text, isW), n);
331 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
334 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
336 LPWSTR wstr = (LPWSTR)text;
338 TRACE("(text=%s, isW=%d)\n", debugtext_t(text, isW), isW);
339 if (!isW && is_textT(text, isW))
341 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
342 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
343 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
345 TRACE(" wstr=%s\n", debugstr_w(wstr));
346 return wstr;
349 static inline void textfreeT(LPWSTR wstr, BOOL isW)
351 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
355 * dest is a pointer to a Unicode string
356 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
358 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
360 BOOL bResult = TRUE;
362 if (src == LPSTR_TEXTCALLBACKW)
364 if (is_textW(*dest)) COMCTL32_Free(*dest);
365 *dest = LPSTR_TEXTCALLBACKW;
367 else
369 LPWSTR pszText = textdupTtoW(src, isW);
370 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
371 bResult = Str_SetPtrW(dest, pszText);
372 textfreeT(pszText, isW);
374 return bResult;
378 * compares a Unicode to a Unicode/ANSI text string
380 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
382 if (!aw) return bt ? -1 : 0;
383 if (!bt) return aw ? 1 : 0;
384 if (aw == LPSTR_TEXTCALLBACKW)
385 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
386 if (bt != LPSTR_TEXTCALLBACKW)
388 LPWSTR bw = textdupTtoW(bt, isW);
389 int r = bw ? lstrcmpW(aw, bw) : 1;
390 textfreeT(bw, isW);
391 return r;
394 return 1;
397 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
398 WPARAM wParam, LPARAM lParam, BOOL isW)
400 if (isW)
401 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
402 else
403 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
406 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
408 pnmh->hwndFrom = infoPtr->hwndSelf;
409 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
410 pnmh->code = code;
411 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
412 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
415 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
417 hwnd_notify(infoPtr->hwndSelf, LVN_ITEMACTIVATE);
420 static inline BOOL listview_notify(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
422 return notify(infoPtr, code, (LPNMHDR)plvnm);
425 static int tabNotification[] = {
426 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
427 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
428 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
429 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
430 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
431 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
435 static int get_ansi_notification(INT unicodeNotificationCode)
437 int *pTabNotif = tabNotification;
438 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
439 if (*pTabNotif) return *pTabNotif;
440 ERR("unknown notification %x\n", unicodeNotificationCode);
441 return unicodeNotificationCode;
445 Send notification. depends on dispinfoW having same
446 structure as dispinfoA.
447 infoPtr : listview struct
448 notificationCode : *Unicode* notification code
449 pdi : dispinfo structure (can be unicode or ansi)
450 isW : TRUE if dispinfo is Unicode
452 static BOOL dispinfo_notifyT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
454 BOOL bResult = FALSE;
455 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
456 INT realNotifCode;
457 INT cchTempBufMax = 0, savCchTextMax = 0;
458 LPWSTR pszTempBuf = NULL, savPszText = NULL;
460 TRACE("(code=%x, pdi=%p, isW=%d)\n", notificationCode, pdi, isW);
461 TRACE(" notifyFormat=%s\n",
462 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
463 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
464 if (infoPtr->notifyFormat == NFR_ANSI)
465 realNotifCode = get_ansi_notification(notificationCode);
466 else
467 realNotifCode = notificationCode;
469 if (is_textT(pdi->item.pszText, isW))
471 if (isW && infoPtr->notifyFormat == NFR_ANSI)
472 convertToAnsi = TRUE;
473 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
474 convertToUnicode = TRUE;
477 if (convertToAnsi || convertToUnicode)
479 TRACE(" we have to convert the text to the correct format\n");
480 if (notificationCode != LVN_GETDISPINFOW)
481 { /* length of existing text */
482 cchTempBufMax = convertToUnicode ?
483 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
484 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
486 else
487 cchTempBufMax = pdi->item.cchTextMax;
489 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
490 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
491 if (!pszTempBuf) return FALSE;
492 if (convertToUnicode)
493 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
494 pszTempBuf, cchTempBufMax);
495 else
496 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
497 cchTempBufMax, NULL, NULL);
498 TRACE(" text=%s\n", debugtext_t(pszTempBuf, convertToUnicode));
499 savCchTextMax = pdi->item.cchTextMax;
500 savPszText = pdi->item.pszText;
501 pdi->item.pszText = pszTempBuf;
502 pdi->item.cchTextMax = cchTempBufMax;
505 bResult = notify(infoPtr, realNotifCode, (LPNMHDR)pdi);
507 if (convertToUnicode || convertToAnsi)
508 { /* convert back result */
509 TRACE(" returned text=%s\n", debugtext_t(pdi->item.pszText, convertToUnicode));
510 if (convertToUnicode) /* note : pointer can be changed by app ! */
511 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
512 savCchTextMax, NULL, NULL);
513 else
514 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
515 savPszText, savCchTextMax);
516 pdi->item.pszText = savPszText; /* restores our buffer */
517 pdi->item.cchTextMax = savCchTextMax;
518 HeapFree(GetProcessHeap(), 0, pszTempBuf);
520 return bResult;
523 static inline LRESULT LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL internal)
525 return LISTVIEW_GetItemT(infoPtr, lpLVItem, internal, TRUE);
528 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
530 int res;
532 n = min(min(n, strlenW(s1)), strlenW(s2));
533 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
534 return res ? res - sizeof(WCHAR) : res;
537 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
539 static int index = 0;
540 static char buffers[20][256];
541 char* buf = buffers[index++ % 20];
542 if (lpLVItem == NULL) return "(null)";
543 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
544 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
545 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
546 lpLVItem->state, lpLVItem->stateMask,
547 debugtext_tn(lpLVItem->pszText, isW, 80),
548 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
549 lpLVItem->iIndent);
550 return buf;
553 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
555 static int index = 0;
556 static char buffers[20][256];
557 char* buf = buffers[index++ % 20];
558 if (lpColumn == NULL) return "(null)";
559 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
560 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
561 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
562 lpColumn->mask & LVCF_TEXT ? debugtext_tn(lpColumn->pszText, isW, 80): "",
563 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
564 return buf;
567 static BOOL
568 LISTVIEW_SendCustomDrawNotify (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, HDC hdc,
569 RECT rc)
571 NMLVCUSTOMDRAW nmcdhdr;
572 LPNMCUSTOMDRAW nmcd;
574 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage, hdc);
576 nmcd= & nmcdhdr.nmcd;
577 nmcd->hdr.hwndFrom = infoPtr->hwndSelf;
578 nmcd->hdr.idFrom = GetWindowLongW( infoPtr->hwndSelf, GWL_ID);
579 nmcd->hdr.code = NM_CUSTOMDRAW;
580 nmcd->dwDrawStage= dwDrawStage;
581 nmcd->hdc = hdc;
582 nmcd->rc.left = rc.left;
583 nmcd->rc.right = rc.right;
584 nmcd->rc.bottom = rc.bottom;
585 nmcd->rc.top = rc.top;
586 nmcd->dwItemSpec = 0;
587 nmcd->uItemState = 0;
588 nmcd->lItemlParam= 0;
589 nmcdhdr.clrText = infoPtr->clrText;
590 nmcdhdr.clrTextBk= infoPtr->clrBk;
592 return (BOOL)SendMessageW (GetParent (infoPtr->hwndSelf), WM_NOTIFY,
593 (WPARAM) nmcd->hdr.idFrom, (LPARAM)&nmcdhdr);
596 static BOOL
597 LISTVIEW_SendCustomDrawItemNotify (LISTVIEW_INFO *infoPtr, HDC hdc,
598 UINT iItem, UINT iSubItem,
599 UINT uItemDrawState)
601 NMLVCUSTOMDRAW nmcdhdr;
602 LPNMCUSTOMDRAW nmcd;
603 DWORD dwDrawStage,dwItemSpec;
604 UINT uItemState;
605 INT retval;
606 RECT itemRect;
607 LVITEMW item;
609 ZeroMemory(&item,sizeof(item));
610 item.iItem = iItem;
611 item.mask = LVIF_PARAM;
612 ListView_GetItemW(infoPtr->hwndSelf,&item);
614 dwDrawStage=CDDS_ITEM | uItemDrawState;
615 dwItemSpec=iItem;
616 uItemState=0;
618 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_SELECTED)) uItemState |= CDIS_SELECTED;
619 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_FOCUSED)) uItemState |= CDIS_FOCUS;
620 if (iItem == infoPtr->nHotItem) uItemState |= CDIS_HOT;
622 itemRect.left = LVIR_BOUNDS;
623 LISTVIEW_GetItemRect(infoPtr, iItem, &itemRect);
625 nmcd= & nmcdhdr.nmcd;
626 nmcd->hdr.hwndFrom = infoPtr->hwndSelf;
627 nmcd->hdr.idFrom = GetWindowLongW( infoPtr->hwndSelf, GWL_ID);
628 nmcd->hdr.code = NM_CUSTOMDRAW;
629 nmcd->dwDrawStage= dwDrawStage;
630 nmcd->hdc = hdc;
631 nmcd->rc.left = itemRect.left;
632 nmcd->rc.right = itemRect.right;
633 nmcd->rc.bottom = itemRect.bottom;
634 nmcd->rc.top = itemRect.top;
635 nmcd->dwItemSpec = dwItemSpec;
636 nmcd->uItemState = uItemState;
637 nmcd->lItemlParam= item.lParam;
638 nmcdhdr.clrText = infoPtr->clrText;
639 nmcdhdr.clrTextBk= infoPtr->clrBk;
640 nmcdhdr.iSubItem =iSubItem;
642 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
643 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
644 nmcd->uItemState, nmcd->lItemlParam);
646 retval=SendMessageW (GetParent (infoPtr->hwndSelf), WM_NOTIFY,
647 (WPARAM)nmcd->hdr.idFrom, (LPARAM)&nmcdhdr);
649 infoPtr->clrText=nmcdhdr.clrText;
650 infoPtr->clrBk =nmcdhdr.clrTextBk;
651 return (BOOL) retval;
654 static inline int LISTVIEW_GetType(LISTVIEW_INFO *infoPtr)
656 return GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
659 /***
660 * DESCRIPTION:
661 * Retrieves the number of items that can fit vertically in the client area.
663 * PARAMETER(S):
664 * [I] infoPtr : valid pointer to the listview structure
666 * RETURN:
667 * Number of items per row.
669 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
671 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
673 return max(nListWidth/infoPtr->nItemWidth, 1);
676 /***
677 * DESCRIPTION:
678 * Retrieves the number of items that can fit horizontally in the client
679 * area.
681 * PARAMETER(S):
682 * [I] infoPtr : valid pointer to the listview structure
684 * RETURN:
685 * Number of items per column.
687 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
689 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
691 return max(nListHeight / infoPtr->nItemHeight, 1);
694 /***
695 * DESCRIPTION:
696 * Retrieves the count of visible items. Note that the returned count may
697 * be a bit larger than the actual count.
699 * PARAMETER(S):
700 * [I] infoPtr : valid pointer to the listview structure
702 * RETURN:
703 * maximum visible item count
705 static INT LISTVIEW_GetVisibleCount(LISTVIEW_INFO *infoPtr)
707 UINT uView = LISTVIEW_GetType(infoPtr);
708 INT nPerCol, nPerRow;
710 if (uView == LVS_REPORT)
712 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
713 nPerRow = 1;
715 else if (uView == LVS_LIST)
717 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
718 nPerRow = LISTVIEW_GetCountPerRow(infoPtr);
720 else
722 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
723 nPerRow = LISTVIEW_GetCountPerRow(infoPtr) + 1;
726 return nPerCol * nPerRow;
730 /*************************************************************************
731 * LISTVIEW_ProcessLetterKeys
733 * Processes keyboard messages generated by pressing the letter keys
734 * on the keyboard.
735 * What this does is perform a case insensitive search from the
736 * current position with the following quirks:
737 * - If two chars or more are pressed in quick succession we search
738 * for the corresponding string (e.g. 'abc').
739 * - If there is a delay we wipe away the current search string and
740 * restart with just that char.
741 * - If the user keeps pressing the same character, whether slowly or
742 * fast, so that the search string is entirely composed of this
743 * character ('aaaaa' for instance), then we search for first item
744 * that starting with that character.
745 * - If the user types the above character in quick succession, then
746 * we must also search for the corresponding string ('aaaaa'), and
747 * go to that string if there is a match.
749 * PARAMETERS
750 * [I] hwnd : handle to the window
751 * [I] charCode : the character code, the actual character
752 * [I] keyData : key data
754 * RETURNS
756 * Zero.
758 * BUGS
760 * - The current implementation has a list of characters it will
761 * accept and it ignores averything else. In particular it will
762 * ignore accentuated characters which seems to match what
763 * Windows does. But I'm not sure it makes sense to follow
764 * Windows there.
765 * - We don't sound a beep when the search fails.
767 * SEE ALSO
769 * TREEVIEW_ProcessLetterKeys
771 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
773 INT nItem;
774 INT nSize;
775 INT endidx,idx;
776 LVITEMW item;
777 WCHAR buffer[MAX_PATH];
778 DWORD timestamp,elapsed;
780 /* simple parameter checking */
781 if (!charCode || !keyData)
782 return 0;
784 if (!infoPtr)
785 return 0;
787 /* only allow the valid WM_CHARs through */
788 if (!isalnum(charCode) &&
789 charCode != '.' && charCode != '`' && charCode != '!' &&
790 charCode != '@' && charCode != '#' && charCode != '$' &&
791 charCode != '%' && charCode != '^' && charCode != '&' &&
792 charCode != '*' && charCode != '(' && charCode != ')' &&
793 charCode != '-' && charCode != '_' && charCode != '+' &&
794 charCode != '=' && charCode != '\\'&& charCode != ']' &&
795 charCode != '}' && charCode != '[' && charCode != '{' &&
796 charCode != '/' && charCode != '?' && charCode != '>' &&
797 charCode != '<' && charCode != ',' && charCode != '~')
798 return 0;
800 nSize=GETITEMCOUNT(infoPtr);
801 /* if there's one item or less, there is no where to go */
802 if (nSize <= 1)
803 return 0;
805 /* compute how much time elapsed since last keypress */
806 timestamp=GetTickCount();
807 if (timestamp > infoPtr->lastKeyPressTimestamp) {
808 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
809 } else {
810 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
813 /* update the search parameters */
814 infoPtr->lastKeyPressTimestamp=timestamp;
815 if (elapsed < KEY_DELAY) {
816 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
817 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
819 if (infoPtr->charCode != charCode) {
820 infoPtr->charCode=charCode=0;
822 } else {
823 infoPtr->charCode=charCode;
824 infoPtr->szSearchParam[0]=charCode;
825 infoPtr->nSearchParamLength=1;
826 /* Redundant with the 1 char string */
827 charCode=0;
830 /* and search from the current position */
831 nItem=-1;
832 if (infoPtr->nFocusedItem >= 0) {
833 endidx=infoPtr->nFocusedItem;
834 idx=endidx;
835 /* if looking for single character match,
836 * then we must always move forward
838 if (infoPtr->nSearchParamLength == 1)
839 idx++;
840 } else {
841 endidx=nSize;
842 idx=0;
844 do {
845 if (idx == nSize) {
846 if (endidx == nSize || endidx == 0)
847 break;
848 idx=0;
851 /* get item */
852 ZeroMemory(&item, sizeof(item));
853 item.mask = LVIF_TEXT;
854 item.iItem = idx;
855 item.iSubItem = 0;
856 item.pszText = buffer;
857 item.cchTextMax = COUNTOF(buffer);
858 ListView_GetItemW(infoPtr->hwndSelf, &item);
860 /* check for a match */
861 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
862 nItem=idx;
863 break;
864 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
865 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
866 /* This would work but we must keep looking for a longer match */
867 nItem=idx;
869 idx++;
870 } while (idx != endidx);
872 if (nItem != -1)
873 LISTVIEW_KeySelection(infoPtr, nItem);
875 return 0;
878 /*************************************************************************
879 * LISTVIEW_UpdateHeaderSize [Internal]
881 * Function to resize the header control
883 * PARAMS
884 * hwnd [I] handle to a window
885 * nNewScrollPos [I] Scroll Pos to Set
887 * RETURNS
888 * nothing
890 * NOTES
892 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
894 RECT winRect;
895 POINT point[2];
897 GetWindowRect(infoPtr->hwndHeader, &winRect);
898 point[0].x = winRect.left;
899 point[0].y = winRect.top;
900 point[1].x = winRect.right;
901 point[1].y = winRect.bottom;
903 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
904 point[0].x = -nNewScrollPos;
905 point[1].x += nNewScrollPos;
907 SetWindowPos(infoPtr->hwndHeader,0,
908 point[0].x,point[0].y,point[1].x,point[1].y,
909 SWP_NOZORDER | SWP_NOACTIVATE);
912 /***
913 * DESCRIPTION:
914 * Update the scrollbars. This functions should be called whenever
915 * the content, size or view changes.
917 * PARAMETER(S):
918 * [I] infoPtr : valid pointer to the listview structure
920 * RETURN:
921 * None
923 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
925 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
926 UINT uView = lStyle & LVS_TYPEMASK;
927 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
928 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
929 SCROLLINFO scrollInfo;
931 if (lStyle & LVS_NOSCROLL) return;
933 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
934 scrollInfo.cbSize = sizeof(SCROLLINFO);
936 if (uView == LVS_LIST)
938 /* update horizontal scrollbar */
939 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
940 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
941 INT nNumOfItems = GETITEMCOUNT(infoPtr);
943 TRACE("items=%d, perColumn=%d, perRow=%d\n",
944 nNumOfItems, nCountPerColumn, nCountPerRow);
946 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
947 if((nNumOfItems % nCountPerColumn) == 0)
948 scrollInfo.nMax--;
949 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
950 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
951 scrollInfo.nPage = nCountPerRow;
952 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
953 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
954 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
956 else if (uView == LVS_REPORT)
958 BOOL test;
960 /* update vertical scrollbar */
961 scrollInfo.nMin = 0;
962 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
963 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
964 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
965 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
966 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
967 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
968 scrollInfo.nMax, scrollInfo.nPage, test);
969 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
970 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
972 /* update horizontal scrollbar */
973 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
974 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo) == FALSE
975 || GETITEMCOUNT(infoPtr) == 0)
977 scrollInfo.nPos = 0;
979 scrollInfo.nMin = 0;
980 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
981 scrollInfo.nPage = nListWidth;
982 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
983 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
984 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
985 scrollInfo.nMax, scrollInfo.nPage, test);
986 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
987 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
989 /* Update the Header Control */
990 scrollInfo.fMask = SIF_POS;
991 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
992 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
995 else
997 RECT rcView;
999 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1001 INT nViewWidth = rcView.right - rcView.left;
1002 INT nViewHeight = rcView.bottom - rcView.top;
1004 /* Update Horizontal Scrollbar */
1005 scrollInfo.fMask = SIF_POS;
1006 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo) == FALSE
1007 || GETITEMCOUNT(infoPtr) == 0)
1009 scrollInfo.nPos = 0;
1011 scrollInfo.nMax = max(nViewWidth, 0)-1;
1012 scrollInfo.nMin = 0;
1013 scrollInfo.nPage = nListWidth;
1014 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1015 TRACE("LVS_ICON/SMALLICON Horz.\n");
1016 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1018 /* Update Vertical Scrollbar */
1019 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1020 scrollInfo.fMask = SIF_POS;
1021 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo) == FALSE
1022 || GETITEMCOUNT(infoPtr) == 0)
1024 scrollInfo.nPos = 0;
1026 scrollInfo.nMax = max(nViewHeight,0)-1;
1027 scrollInfo.nMin = 0;
1028 scrollInfo.nPage = nListHeight;
1029 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1030 TRACE("LVS_ICON/SMALLICON Vert.\n");
1031 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1037 /***
1038 * Toggles (draws/erase) the focus rectangle.
1040 static inline void LISTVIEW_ToggleFocusRect(LISTVIEW_INFO *infoPtr)
1042 /* if we have a focus rectagle, draw it */
1043 if (!IsRectEmpty(&infoPtr->rcFocus))
1045 HDC hdc = GetDC(infoPtr->hwndSelf);
1046 DrawFocusRect(hdc, &infoPtr->rcFocus);
1047 ReleaseDC(infoPtr->hwndSelf, hdc);
1051 /***
1052 * Invalidates all visible selected items.
1054 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1056 INT i, nTop, nBottom;
1058 nTop = LISTVIEW_GetTopIndex(infoPtr);
1059 nBottom = nTop + LISTVIEW_GetVisibleCount(infoPtr);
1060 if (nBottom > GETITEMCOUNT(infoPtr)) nBottom = GETITEMCOUNT(infoPtr);
1062 for (i = nTop; i <= nBottom; i++)
1064 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
1066 RECT rcItem;
1067 rcItem.left = LVIR_BOUNDS;
1068 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
1069 InvalidateRect(infoPtr->hwndSelf, &rcItem, FALSE);
1075 /***
1076 * DESCRIPTION:
1077 * Prints a message for unsupported window styles.
1078 * A kind of TODO list for window styles.
1080 * PARAMETER(S):
1081 * [I] LONG : window style
1083 * RETURN:
1084 * None
1086 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1088 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1089 FIXME(" LVS_NOSCROLL\n");
1091 if (lStyle & LVS_NOLABELWRAP)
1092 FIXME(" LVS_NOLABELWRAP\n");
1094 if (!(lStyle & LVS_SHAREIMAGELISTS))
1095 FIXME(" !LVS_SHAREIMAGELISTS\n");
1097 if (lStyle & LVS_SORTASCENDING)
1098 FIXME(" LVS_SORTASCENDING\n");
1100 if (lStyle & LVS_SORTDESCENDING)
1101 FIXME(" LVS_SORTDESCENDING\n");
1104 /***
1105 * DESCRIPTION:
1106 * Aligns the items with the top edge of the window.
1108 * PARAMETER(S):
1109 * [I] infoPtr : valid pointer to the listview structure
1111 * RETURN:
1112 * None
1114 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1116 UINT uView = LISTVIEW_GetType(infoPtr);
1117 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1118 POINT ptItem;
1119 RECT rcView;
1120 INT i, off_x=0, off_y=0;
1122 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1124 /* Since SetItemPosition uses upper-left of icon, and for
1125 style=LVS_ICON the icon is not left adjusted, get the offset */
1126 if (uView == LVS_ICON)
1128 off_y = ICON_TOP_PADDING;
1129 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1131 ptItem.x = off_x;
1132 ptItem.y = off_y;
1133 ZeroMemory(&rcView, sizeof(RECT));
1134 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1135 off_x, off_y,
1136 infoPtr->rcList.left, infoPtr->rcList.right);
1138 if (nListWidth > infoPtr->nItemWidth)
1140 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1142 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1144 ptItem.x = off_x;
1145 ptItem.y += infoPtr->nItemHeight;
1148 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1149 ptItem.x += infoPtr->nItemWidth;
1150 rcView.right = max(rcView.right, ptItem.x);
1153 rcView.right -= off_x;
1154 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1156 else
1158 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1160 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1161 ptItem.y += infoPtr->nItemHeight;
1164 rcView.right = infoPtr->nItemWidth;
1165 rcView.bottom = ptItem.y-off_y;
1168 LISTVIEW_SetViewRect(infoPtr, &rcView);
1172 /***
1173 * DESCRIPTION:
1174 * Aligns the items with the left edge of the window.
1176 * PARAMETER(S):
1177 * [I] infoPtr : valid pointer to the listview structure
1179 * RETURN:
1180 * None
1182 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1184 UINT uView = LISTVIEW_GetType(infoPtr);
1185 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1186 POINT ptItem;
1187 RECT rcView;
1188 INT i, off_x=0, off_y=0;
1190 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1192 /* Since SetItemPosition uses upper-left of icon, and for
1193 style=LVS_ICON the icon is not left adjusted, get the offset */
1194 if (uView == LVS_ICON)
1196 off_y = ICON_TOP_PADDING;
1197 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1199 ptItem.x = off_x;
1200 ptItem.y = off_y;
1201 ZeroMemory(&rcView, sizeof(RECT));
1202 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1204 if (nListHeight > infoPtr->nItemHeight)
1206 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1208 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1210 ptItem.y = off_y;
1211 ptItem.x += infoPtr->nItemWidth;
1214 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1215 ptItem.y += infoPtr->nItemHeight;
1216 rcView.bottom = max(rcView.bottom, ptItem.y);
1219 rcView.right = ptItem.x + infoPtr->nItemWidth;
1221 else
1223 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1225 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1226 ptItem.x += infoPtr->nItemWidth;
1229 rcView.bottom = infoPtr->nItemHeight;
1230 rcView.right = ptItem.x;
1233 LISTVIEW_SetViewRect(infoPtr, &rcView);
1237 /***
1238 * DESCRIPTION:
1239 * Set the bounding rectangle of all the items.
1241 * PARAMETER(S):
1242 * [I] infoPtr : valid pointer to the listview structure
1243 * [I] LPRECT : bounding rectangle
1245 * RETURN:
1246 * SUCCESS : TRUE
1247 * FAILURE : FALSE
1249 static LRESULT LISTVIEW_SetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1251 BOOL bResult = FALSE;
1253 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1254 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1256 if (lprcView != NULL)
1258 bResult = TRUE;
1259 infoPtr->rcView.left = lprcView->left;
1260 infoPtr->rcView.top = lprcView->top;
1261 infoPtr->rcView.right = lprcView->right;
1262 infoPtr->rcView.bottom = lprcView->bottom;
1265 return bResult;
1268 /***
1269 * DESCRIPTION:
1270 * Retrieves the bounding rectangle of all the items.
1272 * PARAMETER(S):
1273 * [I] infoPtr : valid pointer to the listview structure
1274 * [O] LPRECT : bounding rectangle
1276 * RETURN:
1277 * SUCCESS : TRUE
1278 * FAILURE : FALSE
1280 static LRESULT LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1282 BOOL bResult = FALSE;
1283 POINT ptOrigin;
1285 TRACE("(lprcView=%p)\n", lprcView);
1287 if (lprcView != NULL)
1289 bResult = LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
1290 if (bResult)
1292 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1293 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1294 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1295 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1298 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1299 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1302 return bResult;
1305 /***
1306 * DESCRIPTION:
1307 * Retrieves the subitem pointer associated with the subitem index.
1309 * PARAMETER(S):
1310 * [I] HDPA : DPA handle for a specific item
1311 * [I] INT : index of subitem
1313 * RETURN:
1314 * SUCCESS : subitem pointer
1315 * FAILURE : NULL
1317 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1318 INT nSubItem)
1320 LISTVIEW_SUBITEM *lpSubItem;
1321 INT i;
1323 /* we should binary search here if need be */
1324 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1326 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1327 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1328 return lpSubItem;
1331 return NULL;
1335 /***
1336 * DESCRIPTION:
1337 * Calculates the width of a specific item.
1339 * PARAMETER(S):
1340 * [I] infoPtr : valid pointer to the listview structure
1341 * [I] nItem : item to calculate width, or -1 for max of all
1343 * RETURN:
1344 * Returns the width of an item width an item.
1346 static INT LISTVIEW_CalculateWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1348 UINT uView = LISTVIEW_GetType(infoPtr);
1349 INT nItemWidth = 0, i;
1351 if (uView == LVS_ICON)
1352 nItemWidth = infoPtr->iconSpacing.cx;
1353 else if (uView == LVS_REPORT)
1355 INT nHeaderItemCount;
1356 RECT rcHeaderItem;
1358 /* calculate width of header */
1359 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1360 for (i = 0; i < nHeaderItemCount; i++)
1361 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
1362 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1364 else
1366 INT nLabelWidth;
1368 if (GETITEMCOUNT(infoPtr) == 0) return DEFAULT_COLUMN_WIDTH;
1370 /* get width of string */
1371 if (nItem == -1)
1373 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1375 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1376 nItemWidth = max(nItemWidth, nLabelWidth);
1379 else
1380 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1381 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
1382 nItemWidth += WIDTH_PADDING;
1383 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
1384 if (infoPtr->himlState) nItemWidth += infoPtr->iconSize.cx; /*FIXME: is this correct */
1385 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1388 return max(nItemWidth, 1);
1391 /***
1392 * DESCRIPTION:
1393 * Calculates the max width of any item in the list.
1395 * PARAMETER(S):
1396 * [I] infoPtr : valid pointer to the listview structure
1397 * [I] LONG : window style
1399 * RETURN:
1400 * Returns item width.
1402 static inline INT LISTVIEW_GetItemWidth(LISTVIEW_INFO *infoPtr)
1404 return LISTVIEW_CalculateWidth(infoPtr, -1);
1407 /***
1408 * DESCRIPTION:
1409 * Retrieves and saves important text metrics info for the current
1410 * Listview font.
1412 * PARAMETER(S):
1413 * [I] infoPtr : valid pointer to the listview structure
1416 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1418 TEXTMETRICW tm;
1419 HDC hdc = GetDC(infoPtr->hwndSelf);
1420 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1421 INT oldHeight, oldACW;
1423 GetTextMetricsW(hdc, &tm);
1425 oldHeight = infoPtr->ntmHeight;
1426 oldACW = infoPtr->ntmAveCharWidth;
1427 infoPtr->ntmHeight = tm.tmHeight;
1428 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1430 SelectObject(hdc, hOldFont);
1431 ReleaseDC(infoPtr->hwndSelf, hdc);
1432 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1433 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1437 /***
1438 * DESCRIPTION:
1439 * Calculates the height of an item.
1441 * PARAMETER(S):
1442 * [I] infoPtr : valid pointer to the listview structure
1444 * RETURN:
1445 * Returns item height.
1447 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1449 UINT uView = LISTVIEW_GetType(infoPtr);
1450 INT nItemHeight = 0;
1452 if (uView == LVS_ICON)
1454 nItemHeight = infoPtr->iconSpacing.cy;
1456 else
1458 if(infoPtr->himlState || infoPtr->himlSmall)
1459 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1460 else
1461 nItemHeight = infoPtr->ntmHeight;
1464 return nItemHeight;
1467 #if 0
1468 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1470 LISTVIEW_SELECTION *selection;
1471 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1472 INT i;
1474 TRACE("Selections are:\n");
1475 for (i = 0; i < topSelection; i++)
1477 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1478 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1481 #endif
1483 /***
1484 * DESCRIPTION:
1485 * A compare function for selection ranges
1487 *PARAMETER(S)
1488 * [I] range1 : pointer to selection range 1;
1489 * [I] range2 : pointer to selection range 2;
1490 * [I] flags : flags
1492 *RETURNS:
1493 * >0 : if Item 1 > Item 2
1494 * <0 : if Item 2 > Item 1
1495 * 0 : if Item 1 == Item 2
1497 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2, LPARAM flags)
1499 if (((LISTVIEW_SELECTION*)(range1))->upper <
1500 ((LISTVIEW_SELECTION*)(range2))->lower)
1501 return -1;
1502 if (((LISTVIEW_SELECTION*)(range2))->upper <
1503 ((LISTVIEW_SELECTION*)(range1))->lower)
1504 return 1;
1505 return 0;
1508 /***
1509 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
1511 static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1513 LISTVIEW_SELECTION selection;
1514 LVITEMW lvItem;
1515 INT index, i;
1517 TRACE("range (%i - %i)\n", lower, upper);
1519 /* try find overlapping selections first */
1520 selection.lower = lower - 1;
1521 selection.upper = upper + 1;
1522 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1523 LISTVIEW_CompareSelectionRanges, 0, 0);
1525 if (index == -1)
1527 LISTVIEW_SELECTION *newsel;
1529 /* create the brand new selection to insert */
1530 newsel = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1531 if(!newsel) return FALSE;
1532 newsel->lower = lower;
1533 newsel->upper = upper;
1535 /* figure out where to insert it */
1536 index = DPA_Search(infoPtr->hdpaSelectionRanges, newsel, 0,
1537 LISTVIEW_CompareSelectionRanges, 0, DPAS_INSERTAFTER);
1538 if (index == -1) index = 0;
1540 /* and get it over with */
1541 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
1543 else
1545 LISTVIEW_SELECTION *chksel, *mrgsel;
1546 INT fromindex, mergeindex;
1548 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1549 if (!chksel) return FALSE;
1550 TRACE("Merge with index %i (%lu - %lu)\n",
1551 index,chksel->lower, chksel->upper);
1553 chksel->lower = min(lower, chksel->lower);
1554 chksel->upper = max(upper, chksel->upper);
1556 TRACE("New range %i (%lu - %lu)\n",
1557 index,chksel->lower, chksel->upper);
1559 /* merge now common selection ranges */
1560 fromindex = 0;
1561 selection.lower = chksel->lower - 1;
1562 selection.upper = chksel->upper + 1;
1566 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, fromindex,
1567 LISTVIEW_CompareSelectionRanges, 0, 0);
1568 if (mergeindex == -1) break;
1569 if (mergeindex == index)
1571 fromindex = index + 1;
1572 continue;
1575 TRACE("Merge with index %i\n", mergeindex);
1577 mrgsel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, mergeindex);
1578 if (!mrgsel) return FALSE;
1580 chksel->lower = min(chksel->lower, mrgsel->lower);
1581 chksel->upper = max(chksel->upper, mrgsel->upper);
1582 COMCTL32_Free(mrgsel);
1583 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, mergeindex);
1584 if (mergeindex < index) index --;
1585 } while(1);
1588 /* FIXME: do we really need it? */
1589 DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);
1591 if (adj_sel_only) return TRUE;
1593 /* set the selection on items */
1594 ZeroMemory(&lvItem, sizeof(lvItem));
1595 lvItem.state = LVIS_SELECTED;
1596 lvItem.stateMask = LVIS_SELECTED;
1597 for(i = lower; i <= upper; i++)
1598 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
1600 return TRUE;
1603 /***
1604 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
1606 static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1608 LISTVIEW_SELECTION remsel, tmpsel, *chksel;
1609 BOOL done = FALSE;
1610 LVITEMW lvItem;
1611 INT index, i;
1613 ZeroMemory(&lvItem, sizeof(lvItem));
1614 lvItem.stateMask = LVIS_SELECTED;
1616 remsel.lower = lower;
1617 remsel.upper = upper;
1619 TRACE("range: (%lu - %lu)\n", remsel.lower, remsel.upper);
1623 index = DPA_Search(infoPtr->hdpaSelectionRanges, &remsel, 0,
1624 LISTVIEW_CompareSelectionRanges, 0, 0);
1625 if (index == -1) return TRUE;
1627 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1628 if (!chksel) return FALSE;
1630 TRACE("Matches range index %i (%lu-%lu)\n",
1631 index, chksel->lower, chksel->upper);
1633 /* case 1: Same range */
1634 if ( (chksel->upper == remsel.upper) &&
1635 (chksel->lower == remsel.lower) )
1637 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1638 done = TRUE;
1640 /* case 2: engulf */
1641 else if ( (chksel->upper <= remsel.upper) &&
1642 (chksel->lower >= remsel.lower) )
1644 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1646 /* case 3: overlap upper */
1647 else if ( (chksel->upper < remsel.upper) &&
1648 (chksel->lower < remsel.lower) )
1650 chksel->upper = remsel.lower - 1;
1652 /* case 4: overlap lower */
1653 else if ( (chksel->upper > remsel.upper) &&
1654 (chksel->lower > remsel.lower) )
1656 chksel->lower = remsel.upper + 1;
1658 /* case 5: fully internal */
1659 else
1661 LISTVIEW_SELECTION *newsel =
1662 (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1663 if (!newsel) return FALSE;
1664 tmpsel = *chksel;
1665 newsel->lower = chksel->lower;
1666 newsel->upper = remsel.lower - 1;
1667 chksel->lower = remsel.upper + 1;
1668 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
1669 /*FIXME: why do we need the sort??? */
1670 DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);
1671 chksel = &tmpsel;
1674 if (adj_sel_only) continue;
1676 /* here, chksel holds the selection to delete */
1677 for (i = chksel->lower; i <= chksel->upper; i++)
1678 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
1680 while(!done);
1682 return TRUE;
1686 * DESCRIPTION:
1687 * Adds a selection range.
1689 * PARAMETER(S):
1690 * [I] infoPtr : valid pointer to the listview structure
1691 * [I] lower : lower item index
1692 * [I] upper : upper item index
1694 * RETURN:
1695 * Success: TRUE
1696 * Failure: FALSE
1698 static inline BOOL LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
1700 return add_selection_range(infoPtr, lower, upper, FALSE);
1703 /***
1704 * DESCRIPTION:
1705 * Removes a range selections.
1707 * PARAMETER(S):
1708 * [I] infoPtr : valid pointer to the listview structure
1709 * [I] lower : lower item index
1710 * [I] upper : upper item index
1712 * RETURN:
1713 * Success: TRUE
1714 * Failure: FALSE
1716 static inline BOOL LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
1718 return remove_selection_range(infoPtr, lower, upper, FALSE);
1721 /***
1722 * DESCRIPTION:
1723 * Removes all selection ranges
1725 * Parameters(s):
1726 * [I] infoPtr : valid pointer to the listview structure
1728 * RETURNS:
1729 * SUCCESS : TRUE
1730 * FAILURE : TRUE
1732 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
1734 LISTVIEW_SELECTION *sel;
1735 static BOOL removing_all_selections = FALSE;
1737 if (removing_all_selections) return TRUE;
1739 removing_all_selections = TRUE;
1741 TRACE("()\n");
1745 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1746 if (sel) LISTVIEW_RemoveSelectionRange(infoPtr, sel->lower, sel->upper);
1748 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
1750 removing_all_selections = FALSE;
1752 return TRUE;
1755 /***
1756 * DESCRIPTION:
1757 * Manages the item focus.
1759 * PARAMETER(S):
1760 * [I] infoPtr : valid pointer to the listview structure
1761 * [I] INT : item index
1763 * RETURN:
1764 * TRUE : focused item changed
1765 * FALSE : focused item has NOT changed
1767 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
1769 INT oldFocus = infoPtr->nFocusedItem;
1770 LVITEMW lvItem;
1772 ZeroMemory(&lvItem, sizeof(lvItem));
1773 lvItem.state = LVIS_FOCUSED;
1774 lvItem.stateMask = LVIS_FOCUSED;
1775 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
1777 return oldFocus != infoPtr->nFocusedItem;
1781 * DESCRIPTION:
1782 * Updates the various indices after an item has been inserted or deleted.
1784 * PARAMETER(S):
1785 * [I] infoPtr : valid pointer to the listview structure
1786 * [I] INT : item index
1787 * [I] INT : Direction of shift, +1 or -1.
1789 * RETURN:
1790 * None
1792 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
1794 LISTVIEW_SELECTION selection,*checkselection;
1795 INT index;
1797 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1799 selection.upper = nItem;
1800 selection.lower = nItem;
1802 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1803 LISTVIEW_CompareSelectionRanges,
1804 0,DPAS_SORTED|DPAS_INSERTAFTER);
1806 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1808 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1809 if ((checkselection->lower >= nItem)&&
1810 ((int)(checkselection->lower + direction) >= 0))
1811 checkselection->lower += direction;
1812 if ((checkselection->upper >= nItem)&&
1813 ((int)(checkselection->upper + direction) >= 0))
1814 checkselection->upper += direction;
1815 index ++;
1818 /* Note that the following will fail if direction != +1 and -1 */
1819 if (infoPtr->nSelectionMark > nItem)
1820 infoPtr->nSelectionMark += direction;
1821 else if (infoPtr->nSelectionMark == nItem)
1823 if (direction > 0)
1824 infoPtr->nSelectionMark += direction;
1825 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1826 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1829 if (infoPtr->nFocusedItem > nItem)
1830 infoPtr->nFocusedItem += direction;
1831 else if (infoPtr->nFocusedItem == nItem)
1833 if (direction > 0)
1834 infoPtr->nFocusedItem += direction;
1835 else
1837 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1838 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1839 if (infoPtr->nFocusedItem >= 0)
1840 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
1843 /* But we are not supposed to modify nHotItem! */
1848 * DESCRIPTION:
1849 * Adds a block of selections.
1851 * PARAMETER(S):
1852 * [I] infoPtr : valid pointer to the listview structure
1853 * [I] INT : item index
1855 * RETURN:
1856 * None
1858 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
1860 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1861 INT nLast = max(infoPtr->nSelectionMark, nItem);
1862 INT i;
1863 LVITEMW item;
1865 if (nFirst == -1)
1866 nFirst = nItem;
1868 ZeroMemory(&item,sizeof(item));
1869 item.stateMask = LVIS_SELECTED;
1870 item.state = LVIS_SELECTED;
1872 /* FIXME: this is not correct LVS_OWNERDATA
1873 * See docu for LVN_ITEMCHANGED. Is there something similar for
1874 * RemoveGroupSelection (is there such a thing?)?
1876 for (i = nFirst; i <= nLast; i++)
1877 LISTVIEW_SetItemState(infoPtr,i,&item);
1879 LISTVIEW_SetItemFocus(infoPtr, nItem);
1880 infoPtr->nSelectionMark = nItem;
1884 /***
1885 * DESCRIPTION:
1886 * Adds a single selection.
1888 * PARAMETER(S):
1889 * [I] infoPtr : valid pointer to the listview structure
1890 * [I] INT : item index
1892 * RETURN:
1893 * None
1895 static void LISTVIEW_AddSelection(LISTVIEW_INFO *infoPtr, INT nItem)
1897 LVITEMW item;
1899 ZeroMemory(&item,sizeof(item));
1900 item.state = LVIS_SELECTED;
1901 item.stateMask = LVIS_SELECTED;
1903 LISTVIEW_SetItemState(infoPtr,nItem,&item);
1905 LISTVIEW_SetItemFocus(infoPtr, nItem);
1906 infoPtr->nSelectionMark = nItem;
1909 /***
1910 * DESCRIPTION:
1911 * Selects items based on view coordinates.
1913 * PARAMETER(S):
1914 * [I] infoPtr : valid pointer to the listview structure
1915 * [I] rcSelRect : selection rectangle
1917 * RETURN:
1918 * None
1920 static void LISTVIEW_SetSelectionRect(LISTVIEW_INFO *infoPtr, RECT rcSelRect)
1922 POINT ptItem;
1923 INT i;
1924 LVITEMW item;
1926 ZeroMemory(&item,sizeof(item));
1927 item.stateMask = LVIS_SELECTED;
1929 /* FIXME: shouldn't we iterate over visible items only? */
1930 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1932 LISTVIEW_GetItemPosition(infoPtr, i, &ptItem);
1934 item.state = PtInRect(&rcSelRect, ptItem) ? LVIS_SELECTED : 0;
1935 LISTVIEW_SetItemState(infoPtr,i,&item);
1939 /***
1940 * DESCRIPTION:
1941 * Sets a single group selection.
1943 * PARAMETER(S):
1944 * [I] infoPtr : valid pointer to the listview structure
1945 * [I] INT : item index
1947 * RETURN:
1948 * None
1950 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
1952 UINT uView = LISTVIEW_GetType(infoPtr);
1953 LVITEMW item;
1955 ZeroMemory(&item,sizeof(item));
1956 item.stateMask = LVIS_SELECTED;
1958 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
1960 INT i;
1961 INT nFirst, nLast;
1963 if (infoPtr->nSelectionMark == -1)
1965 infoPtr->nSelectionMark = nFirst = nLast = nItem;
1967 else
1969 nFirst = min(infoPtr->nSelectionMark, nItem);
1970 nLast = max(infoPtr->nSelectionMark, nItem);
1973 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
1975 if ((i < nFirst) || (i > nLast))
1976 item.state = 0;
1977 else
1978 item.state = LVIS_SELECTED;
1979 LISTVIEW_SetItemState(infoPtr,i,&item);
1982 else
1984 RECT rcItem;
1985 RECT rcSelMark;
1986 RECT rcSel;
1987 LISTVIEW_GetItemBoundBox(infoPtr, nItem, &rcItem);
1988 LISTVIEW_GetItemBoundBox(infoPtr, infoPtr->nSelectionMark, &rcSelMark);
1989 rcSel.left = min(rcSelMark.left, rcItem.left);
1990 rcSel.top = min(rcSelMark.top, rcItem.top);
1991 rcSel.right = max(rcSelMark.right, rcItem.right);
1992 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
1993 LISTVIEW_SetSelectionRect(infoPtr, rcSel);
1994 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
1995 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
1996 infoPtr->nSelectionMark,
1997 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
1998 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2002 LISTVIEW_SetItemFocus(infoPtr, nItem);
2005 /***
2006 * DESCRIPTION:
2007 * Sets a single selection.
2009 * PARAMETER(S):
2010 * [I] infoPtr : valid pointer to the listview structure
2011 * [I] INT : item index
2013 * RETURN:
2014 * None
2016 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2018 LVITEMW lvItem;
2020 LISTVIEW_RemoveAllSelections(infoPtr);
2022 ZeroMemory(&lvItem, sizeof(lvItem));
2023 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2024 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2025 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2027 infoPtr->nSelectionMark = nItem;
2030 /***
2031 * DESCRIPTION:
2032 * Set selection(s) with keyboard.
2034 * PARAMETER(S):
2035 * [I] infoPtr : valid pointer to the listview structure
2036 * [I] INT : item index
2038 * RETURN:
2039 * SUCCESS : TRUE (needs to be repainted)
2040 * FAILURE : FALSE (nothing has changed)
2042 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2044 /* FIXME: pass in the state */
2045 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2046 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2047 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2048 BOOL bResult = FALSE;
2050 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2052 if (lStyle & LVS_SINGLESEL)
2054 bResult = TRUE;
2055 LISTVIEW_SetSelection(infoPtr, nItem);
2056 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2058 else
2060 if (wShift)
2062 bResult = TRUE;
2063 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2065 else if (wCtrl)
2067 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2069 else
2071 bResult = TRUE;
2072 LISTVIEW_SetSelection(infoPtr, nItem);
2073 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2078 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2079 return bResult;
2082 /***
2083 * DESCRIPTION:
2084 * Selects an item based on coordinates.
2086 * PARAMETER(S):
2087 * [I] infoPtr : valid pointer to the listview structure
2088 * [I] pt : mouse click ccordinates
2090 * RETURN:
2091 * SUCCESS : item index
2092 * FAILURE : -1
2094 static INT LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, POINT pt)
2096 INT i, nTop, nBottom;
2097 RECT rcItem;
2099 nTop = LISTVIEW_GetTopIndex(infoPtr);
2100 nBottom = nTop + LISTVIEW_GetVisibleCount(infoPtr);
2101 if (nBottom > GETITEMCOUNT(infoPtr)) nBottom = GETITEMCOUNT(infoPtr);
2103 for (i = nTop; i <= nBottom; i++)
2105 rcItem.left = LVIR_SELECTBOUNDS;
2106 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
2107 if (PtInRect(&rcItem, pt)) return i;
2109 return -1;
2112 /***
2113 * DESCRIPTION:
2114 * Called when the mouse is being actively tracked and has hovered for a specified
2115 * amount of time
2117 * PARAMETER(S):
2118 * [I] infoPtr : valid pointer to the listview structure
2119 * [I] fwKeys : key indicator
2120 * [I] pts : mouse position
2122 * RETURN:
2123 * 0 if the message was processed, non-zero if there was an error
2125 * INFO:
2126 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2127 * over the item for a certain period of time.
2130 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2132 POINT pt = { pts.x, pts.y };
2134 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT)
2135 /* FIXME: select the item!!! */
2136 LISTVIEW_GetItemAtPt(infoPtr, pt);
2138 return 0;
2141 /***
2142 * DESCRIPTION:
2143 * Called whenever WM_MOUSEMOVE is received.
2145 * PARAMETER(S):
2146 * [I] infoPtr : valid pointer to the listview structure
2147 * [I] fwKeys : key indicator
2148 * [I] pts : mouse position
2150 * RETURN:
2151 * 0 if the message is processed, non-zero if there was an error
2153 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2155 TRACKMOUSEEVENT trackinfo;
2157 /* see if we are supposed to be tracking mouse hovering */
2158 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2159 /* fill in the trackinfo struct */
2160 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2161 trackinfo.dwFlags = TME_QUERY;
2162 trackinfo.hwndTrack = infoPtr->hwndSelf;
2163 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2165 /* see if we are already tracking this hwnd */
2166 _TrackMouseEvent(&trackinfo);
2168 if(!(trackinfo.dwFlags & TME_HOVER)) {
2169 trackinfo.dwFlags = TME_HOVER;
2171 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2172 _TrackMouseEvent(&trackinfo);
2176 return 0;
2179 /***
2180 * DESCRIPTION:
2181 * Removes a column.
2183 * PARAMETER(S):
2184 * [IO] HDPA : dynamic pointer array handle
2185 * [I] INT : column index (subitem index)
2187 * RETURN:
2188 * SUCCCESS : TRUE
2189 * FAILURE : FALSE
2191 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2193 BOOL bResult = TRUE;
2194 HDPA hdpaSubItems;
2195 INT i;
2197 for (i = 0; i < hdpaItems->nItemCount; i++)
2199 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2200 if (hdpaSubItems != NULL)
2202 if (!LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem))
2204 bResult = FALSE;
2209 return bResult;
2212 /***
2213 * DESCRIPTION:
2214 * Removes a subitem at a given position.
2216 * PARAMETER(S):
2217 * [IO] HDPA : dynamic pointer array handle
2218 * [I] INT : subitem index
2220 * RETURN:
2221 * SUCCCESS : TRUE
2222 * FAILURE : FALSE
2224 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2226 LISTVIEW_SUBITEM *lpSubItem;
2227 INT i;
2229 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2231 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2232 if (lpSubItem != NULL)
2234 if (lpSubItem->iSubItem == nSubItem)
2236 /* free string */
2237 if (is_textW(lpSubItem->hdr.pszText))
2238 COMCTL32_Free(lpSubItem->hdr.pszText);
2240 /* free item */
2241 COMCTL32_Free(lpSubItem);
2243 /* free dpa memory */
2244 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2245 return FALSE;
2247 else if (lpSubItem->iSubItem > nSubItem)
2248 return TRUE;
2252 return TRUE;
2256 /***
2257 * Tests wheather the item is assignable to a list with style lStyle
2259 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2261 if ( (lpLVItem->mask & LVIF_TEXT) &&
2262 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2263 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2265 return TRUE;
2268 /***
2269 * DESCRIPTION:
2270 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2272 * PARAMETER(S):
2273 * [I] infoPtr : valid pointer to the listview structure
2274 * [I] lpLVItem : valid pointer to new item atttributes
2275 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2277 * RETURN:
2278 * SUCCESS : TRUE
2279 * FAILURE : FALSE
2281 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2283 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2284 UINT uView = lStyle & LVS_TYPEMASK;
2285 HDPA hdpaSubItems;
2286 LISTVIEW_ITEM *lpItem;
2287 NMLISTVIEW nmlv;
2288 UINT uChanged = 0;
2290 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2293 if (lStyle & LVS_OWNERDATA)
2295 INT oldState;
2297 /* a virtual livst view stores only state for the mai item */
2298 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2300 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2302 /* we're done if we don't need to change anything we handle */
2303 if ( (oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2304 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED)) return FALSE;
2307 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2308 * by LVS_OWERNDATA list controls
2311 /* if we handle the focus, and we're asked to change it, do it now */
2312 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2314 if (lpLVItem->state & LVIS_FOCUSED)
2315 infoPtr->nFocusedItem = lpLVItem->iItem;
2316 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2317 infoPtr->nFocusedItem = -1;
2320 /* and the selection is the only other state a virtual list may hold */
2321 if (lpLVItem->stateMask & LVIS_SELECTED)
2323 if (lpLVItem->state & LVIS_SELECTED)
2325 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2326 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2328 else
2329 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2332 /* notify the parent now that things have changed */
2333 ZeroMemory(&nmlv, sizeof(nmlv));
2334 nmlv.iItem = lpLVItem->iItem;
2335 nmlv.uNewState = lpLVItem->state;
2336 nmlv.uOldState = oldState;
2337 nmlv.uChanged = LVIF_STATE;
2338 listview_notify(infoPtr, LVN_ITEMCHANGED, &nmlv);
2340 return TRUE;
2343 /* sanity checks first */
2344 if (!is_assignable_item(lpLVItem, lStyle)) return FALSE;
2346 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2347 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2349 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2350 if (!lpItem) return FALSE;
2352 /* determine what fields will change */
2353 if ((lpLVItem->mask & LVIF_STATE) &&
2354 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask))
2355 uChanged |= LVIF_STATE;
2357 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2358 uChanged |= LVIF_IMAGE;
2360 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2361 uChanged |= LVIF_PARAM;
2363 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2364 uChanged |= LVIF_INDENT;
2366 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2367 uChanged |= LVIF_TEXT;
2369 if (!uChanged) return TRUE;
2371 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2372 nmlv.iItem = lpLVItem->iItem;
2373 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2374 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2375 nmlv.uChanged = uChanged;
2376 nmlv.lParam = lpItem->lParam;
2378 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2379 if(lpItem->valid && listview_notify(infoPtr, LVN_ITEMCHANGING, &nmlv))
2380 return FALSE;
2382 /* copy information */
2383 if (lpLVItem->mask & LVIF_TEXT)
2384 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2386 if (lpLVItem->mask & LVIF_IMAGE)
2387 lpItem->hdr.iImage = lpLVItem->iImage;
2389 if (lpLVItem->mask & LVIF_PARAM)
2390 lpItem->lParam = lpLVItem->lParam;
2392 if (lpLVItem->mask & LVIF_INDENT)
2393 lpItem->iIndent = lpLVItem->iIndent;
2395 if (uChanged & LVIF_STATE)
2397 lpItem->state &= ~lpLVItem->stateMask;
2398 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2399 if (nmlv.uNewState & LVIS_SELECTED)
2401 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2402 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2404 else if (lpLVItem->stateMask & LVIS_SELECTED)
2405 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2407 /* if we are asked to change focus, and we manage it, do it */
2408 if (nmlv.uNewState & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2410 if (lpLVItem->state & LVIS_FOCUSED)
2412 infoPtr->nFocusedItem = lpLVItem->iItem;
2413 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2415 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2416 infoPtr->nFocusedItem = -1;
2420 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2421 if((uChanged & LVIF_TEXT) && ((uView == LVS_LIST) || (uView == LVS_SMALLICON)))
2423 int item_width = LISTVIEW_CalculateWidth(infoPtr, lpLVItem->iItem);
2424 if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width;
2427 /* if we're inserting the item, we're done */
2428 if (!lpItem->valid) return TRUE;
2430 /* send LVN_ITEMCHANGED notification */
2431 nmlv.lParam = lpItem->lParam;
2432 listview_notify(infoPtr, LVN_ITEMCHANGED, &nmlv);
2434 return TRUE;
2437 /***
2438 * DESCRIPTION:
2439 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2441 * PARAMETER(S):
2442 * [I] infoPtr : valid pointer to the listview structure
2443 * [I] lpLVItem : valid pointer to new subitem atttributes
2444 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2446 * RETURN:
2447 * SUCCESS : TRUE
2448 * FAILURE : FALSE
2450 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2452 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2453 HDPA hdpaSubItems;
2454 LISTVIEW_SUBITEM *lpSubItem;
2456 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2458 if (lStyle & LVS_OWNERDATA) return FALSE;
2460 /* set subitem only if column is present */
2461 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2462 return FALSE;
2464 /* First do some sanity checks */
2465 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2467 if (!is_assignable_item(lpLVItem, lStyle)) return FALSE;
2469 /* get the subitem structure, and create it if not there */
2470 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2471 if (!hdpaSubItems) return FALSE;
2473 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2474 if (!lpSubItem)
2476 LISTVIEW_SUBITEM *tmpSubItem;
2477 INT i;
2479 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2480 if (!lpSubItem) return FALSE;
2481 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2483 /* we could binary search here, if need be...*/
2484 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2486 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2487 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2489 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2491 COMCTL32_Free(lpSubItem);
2492 return FALSE;
2496 lpSubItem->iSubItem = lpLVItem->iSubItem;
2498 if (lpLVItem->mask & LVIF_IMAGE)
2499 lpSubItem->hdr.iImage = lpLVItem->iImage;
2501 if (lpLVItem->mask & LVIF_TEXT)
2502 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2504 return TRUE;
2507 /***
2508 * DESCRIPTION:
2509 * Sets item attributes.
2511 * PARAMETER(S):
2512 * [I] infoPtr : valid pointer to the listview structure
2513 * [I] LPLVITEM : new item atttributes
2514 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2516 * RETURN:
2517 * SUCCESS : TRUE
2518 * FAILURE : FALSE
2520 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2522 INT oldFocus = infoPtr->nFocusedItem;
2523 LPWSTR pszText = NULL;
2524 RECT rcItem;
2525 BOOL bResult;
2527 if (!lpLVItem || lpLVItem->iItem < 0 ||
2528 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2529 return FALSE;
2531 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2532 if ((lpLVItem->mask & LVIF_TEXT) && lpLVItem->pszText)
2534 pszText = lpLVItem->pszText;
2535 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
2537 /* actually set the fields */
2538 if (lpLVItem->iSubItem)
2539 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
2540 else
2541 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
2542 /* redraw item, if necessary */
2543 if (bResult && !infoPtr->bIsDrawing)
2545 if (oldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
2546 LISTVIEW_ToggleFocusRect(infoPtr);
2547 rcItem.left = LVIR_BOUNDS;
2548 LISTVIEW_GetItemRect(infoPtr, lpLVItem->iItem, &rcItem);
2549 InvalidateRect(infoPtr->hwndSelf, &rcItem, TRUE);
2551 /* restore text */
2552 if (pszText)
2554 textfreeT(lpLVItem->pszText, isW);
2555 lpLVItem->pszText = pszText;
2558 return bResult;
2561 /***
2562 * DESCRIPTION:
2563 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2565 * PARAMETER(S):
2566 * [I] infoPtr : valid pointer to the listview structure
2568 * RETURN:
2569 * item index
2571 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2573 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2574 UINT uView = lStyle & LVS_TYPEMASK;
2575 INT nItem = 0;
2576 SCROLLINFO scrollInfo;
2578 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2579 scrollInfo.cbSize = sizeof(SCROLLINFO);
2580 scrollInfo.fMask = SIF_POS;
2582 if (uView == LVS_LIST)
2584 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2585 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2587 else if (uView == LVS_REPORT)
2589 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2590 nItem = scrollInfo.nPos;
2593 return nItem;
2597 /***
2598 * DESCRIPTION:
2599 * Draws a subitem.
2601 * PARAMETER(S):
2602 * [I] infoPtr : valid pointer to the listview structure
2603 * [I] HDC : device context handle
2604 * [I] INT : item index
2605 * [I] INT : subitem index
2606 * [I] RECT * : clipping rectangle
2608 * RETURN:
2609 * None
2611 static void LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem,
2612 RECT rcItem, BOOL Selected)
2614 WCHAR szDispText[DISP_TEXT_SIZE];
2615 LVITEMW lvItem;
2616 LVCOLUMNW lvColumn;
2617 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2618 RECT rcTemp;
2619 INT textLeft;
2620 INT nLabelWidth = 0;
2622 TRACE("(hdc=%x, nItem=%d, nSubItem=%d)\n", hdc,
2623 nItem, nSubItem);
2625 /* get information needed for drawing the item */
2626 ZeroMemory(&lvItem, sizeof(lvItem));
2627 lvItem.mask = LVIF_TEXT;
2628 lvItem.iItem = nItem;
2629 lvItem.iSubItem = nSubItem;
2630 lvItem.cchTextMax = DISP_TEXT_SIZE;
2631 lvItem.pszText = szDispText;
2632 *lvItem.pszText = '\0';
2633 LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE);
2634 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2636 ZeroMemory(&lvColumn, sizeof(lvColumn));
2637 lvColumn.mask = LVCF_FMT;
2638 LISTVIEW_GetColumnT(infoPtr, nSubItem, &lvColumn, TRUE);
2639 textLeft = rcItem.left;
2640 TRACE("lvColumn.fmt=%d\n", lvColumn.fmt);
2641 if (lvColumn.fmt != LVCFMT_LEFT)
2643 if ((nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE)))
2645 if (lvColumn.fmt == LVCFMT_RIGHT)
2646 textLeft = rcItem.right - nLabelWidth;
2647 else
2648 textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
2653 /* redraw the background of the item */
2654 rcTemp = rcItem;
2655 if(infoPtr->nColumnCount == (nSubItem + 1))
2656 rcTemp.right = infoPtr->rcList.right;
2657 else
2658 rcTemp.right += WIDTH_PADDING;
2660 if (infoPtr->clrBk != CLR_NONE) FillRect(hdc, &rcTemp, infoPtr->hBkBrush);
2662 /* set item colors */
2663 if (ListView_GetItemState(infoPtr->hwndSelf,nItem,LVIS_SELECTED) && Selected)
2665 if (infoPtr->bFocus)
2667 SetBkColor(hdc, comctl32_color.clrHighlight);
2668 SetTextColor(hdc, comctl32_color.clrHighlightText);
2670 else
2672 SetBkColor(hdc, comctl32_color.clr3dFace);
2673 SetTextColor(hdc, comctl32_color.clrBtnText);
2676 else
2678 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2680 SetBkMode(hdc, TRANSPARENT);
2681 textoutOptions &= ~ETO_OPAQUE;
2683 else
2685 SetBkMode(hdc, OPAQUE);
2686 SetBkColor(hdc, infoPtr->clrTextBk);
2689 SetTextColor(hdc, infoPtr->clrText);
2692 TRACE("drawing text %s, l=%d, t=%d, rect=(%d,%d)-(%d,%d)\n",
2693 debugstr_w(lvItem.pszText), textLeft, rcItem.top,
2694 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
2695 ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
2696 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
2698 if (Selected)
2700 /* fill in the gap */
2701 RECT rec;
2702 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
2704 CopyRect(&rec,&rcItem);
2705 rec.left = rec.right;
2706 rec.right = rec.left+REPORT_MARGINX;
2707 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
2708 &rec, NULL, 0, NULL);
2710 CopyRect(&rec,&rcItem);
2711 rec.right = rec.left;
2712 rec.left = rec.left - REPORT_MARGINX;
2713 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
2714 &rec, NULL, 0, NULL);
2719 /***
2720 * DESCRIPTION:
2721 * Draws an item.
2723 * PARAMETER(S):
2724 * [I] infoPtr : valid pointer to the listview structure
2725 * [I] hdc : device context handle
2726 * [I] nItem : item index
2727 * [I] rcItem : item rectangle
2728 * [I] bFullSelect : TRUE if all item is selected
2729 * [O] lprcFocus : pointer to rectangle to receive focus rect
2731 * RETURN:
2732 * None
2734 static void LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem, BOOL bFullSelect, RECT* lprcFocus)
2736 WCHAR szDispText[DISP_TEXT_SIZE];
2737 INT nLabelWidth;
2738 LVITEMW lvItem;
2739 INT nMixMode;
2740 DWORD dwBkColor;
2741 DWORD dwTextColor,dwTextX;
2742 BOOL bImage = FALSE;
2743 INT iBkMode = -1;
2744 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
2745 RECT rcTemp;
2747 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
2749 /* get information needed for drawing the item */
2750 ZeroMemory(&lvItem, sizeof(lvItem));
2751 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2752 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
2753 lvItem.iItem = nItem;
2754 lvItem.iSubItem = 0;
2755 lvItem.cchTextMax = DISP_TEXT_SIZE;
2756 lvItem.pszText = szDispText;
2757 *lvItem.pszText = '\0';
2758 LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE);
2759 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2761 /* redraw the background of the item */
2762 rcTemp = rcItem;
2763 if(infoPtr->nColumnCount == (nItem + 1))
2764 rcTemp.right = infoPtr->rcList.right;
2765 else
2766 rcTemp.right+=WIDTH_PADDING;
2768 if (infoPtr->clrBk != CLR_NONE) FillRect(hdc, &rcTemp, infoPtr->hBkBrush);
2770 /* do indent */
2771 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
2773 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
2776 /* state icons */
2777 if (infoPtr->himlState != NULL)
2779 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
2780 if (uStateImage > 0)
2781 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
2782 rcItem.top, ILD_NORMAL);
2784 rcItem.left += infoPtr->iconSize.cx;
2785 bImage = TRUE;
2788 /* small icons */
2789 if (infoPtr->himlSmall != NULL)
2791 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) &&
2792 (lvItem.iImage>=0))
2794 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2795 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
2796 rcItem.top, ILD_SELECTED);
2798 else if (lvItem.iImage>=0)
2800 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2801 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
2802 rcItem.top, ILD_NORMAL);
2805 rcItem.left += infoPtr->iconSize.cx;
2806 bImage = TRUE;
2809 /* Don't bother painting item being edited */
2810 if (infoPtr->bEditing && lprcFocus && !bFullSelect)
2812 SetRectEmpty(lprcFocus);
2813 return;
2816 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus))
2818 /* set item colors */
2819 dwBkColor = SetBkColor(hdc, comctl32_color.clrHighlight);
2820 dwTextColor = SetTextColor(hdc, comctl32_color.clrHighlightText);
2821 /* set raster mode */
2822 nMixMode = SetROP2(hdc, R2_XORPEN);
2824 else if ((GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
2825 (lvItem.state & LVIS_SELECTED) && (!infoPtr->bFocus))
2827 dwBkColor = SetBkColor(hdc, comctl32_color.clr3dFace);
2828 dwTextColor = SetTextColor(hdc, comctl32_color.clrBtnText);
2829 /* set raster mode */
2830 nMixMode = SetROP2(hdc, R2_COPYPEN);
2832 else
2834 /* set item colors */
2835 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2837 dwBkColor = GetBkColor(hdc);
2838 iBkMode = SetBkMode(hdc, TRANSPARENT);
2839 textoutOptions &= ~ETO_OPAQUE;
2841 else
2843 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
2844 iBkMode = SetBkMode(hdc, OPAQUE);
2847 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
2848 /* set raster mode */
2849 nMixMode = SetROP2(hdc, R2_COPYPEN);
2852 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
2853 if (rcItem.left + nLabelWidth < rcItem.right)
2855 if (!bFullSelect)
2856 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
2857 if (bImage)
2858 rcItem.right += IMAGE_PADDING;
2861 /* draw label */
2862 dwTextX = rcItem.left + 1;
2863 if (bImage)
2864 dwTextX += IMAGE_PADDING;
2866 /* compute the focus rectangle */
2867 if(lprcFocus)
2869 if (lvItem.pszText)
2871 *lprcFocus = rcItem;
2872 lprcFocus->right -= 2;
2874 else SetRectEmpty(lprcFocus);
2877 if (lvItem.pszText)
2879 TRACE("drawing text rect=(%d,%d)-(%d,%d)\n",
2880 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
2881 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
2882 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
2885 if ((bFullSelect) && (Header_GetItemCount(infoPtr->hwndHeader) > 1))
2887 /* fill in the gap */
2888 RECT rec;
2889 CopyRect(&rec,&rcItem);
2890 rec.left = rec.right;
2891 rec.right = rec.left+REPORT_MARGINX;
2892 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
2893 &rec, NULL, 0, NULL);
2896 if (nMixMode != 0)
2898 SetROP2(hdc, R2_COPYPEN);
2899 SetBkColor(hdc, dwBkColor);
2900 SetTextColor(hdc, dwTextColor);
2901 if (iBkMode != -1)
2902 SetBkMode(hdc, iBkMode);
2906 /***
2907 * DESCRIPTION:
2908 * Draws an item when in large icon display mode.
2910 * PARAMETER(S):
2911 * [I] infoPtr : valid pointer to the listview structure
2912 * [I] hdc : device context handle
2913 * [I] nItem : item index
2914 * [I] rcItem : clipping rectangle
2915 * [O] lprcFocus : the text rectangle about which to draw the focus
2917 * RETURN:
2918 * None
2920 static void LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem,
2921 RECT *lprcFocus)
2923 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2924 LVITEMW lvItem;
2925 UINT uFormat = LISTVIEW_DTFLAGS;
2926 RECT rcFill;
2928 TRACE("(hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
2929 hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
2931 /* get information needed for drawing the item */
2932 ZeroMemory(&lvItem, sizeof(lvItem));
2933 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
2934 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
2935 lvItem.iItem = nItem;
2936 lvItem.iSubItem = 0;
2937 lvItem.cchTextMax = DISP_TEXT_SIZE;
2938 lvItem.pszText = szDispText;
2939 *lvItem.pszText = '\0';
2940 LISTVIEW_GetItemW(infoPtr, &lvItem, FALSE);
2941 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2943 rcFill = rcItem;
2945 TRACE("background rect (%d,%d)-(%d,%d)\n",
2946 rcFill.left, rcFill.top, rcFill.right, rcFill.bottom);
2948 if (infoPtr->clrBk != CLR_NONE) FillRect(hdc, &rcFill, infoPtr->hBkBrush);
2950 /* Figure out text colours etc. depending on state
2951 * At least the following states exist; there may be more.
2952 * Many items may be selected
2953 * At most one item may have the focus
2954 * The application may not actually be active currently
2955 * 1. The item is not selected in any way
2956 * 2. The cursor is flying over the icon or text and the text is being
2957 * expanded because it is not fully displayed currently.
2958 * 3. The item is selected and is focussed, i.e. the user has not clicked
2959 * in the blank area of the window, and the window (or application?)
2960 * still has the focus.
2961 * 4. As 3 except that a different window has the focus
2962 * 5. The item is the selected item of all the items, but the user has
2963 * clicked somewhere else on the window.
2964 * Only a few of these are handled currently. In particular 2 is not yet
2965 * handled since we do not support the functionality currently (or at least
2966 * we didn't when I wrote this)
2969 if (lvItem.state & LVIS_SELECTED)
2971 /* set item colors */
2972 SetBkColor(hdc, comctl32_color.clrHighlight);
2973 SetTextColor(hdc, comctl32_color.clrHighlightText);
2974 SetBkMode (hdc, OPAQUE);
2975 /* set raster mode */
2976 SetROP2(hdc, R2_XORPEN);
2977 /* When exactly is it in XOR? while being dragged? */
2979 else
2981 /* set item colors */
2982 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2984 SetBkMode(hdc, TRANSPARENT);
2986 else
2988 SetBkMode(hdc, OPAQUE);
2989 SetBkColor(hdc, infoPtr->clrTextBk);
2992 SetTextColor(hdc, infoPtr->clrText);
2993 /* set raster mode */
2994 SetROP2(hdc, R2_COPYPEN);
2997 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
2998 * wrapping and long words split.
2999 * In cases 1 and 4 only a portion of the text is displayed with word
3000 * wrapping and both word and end ellipsis. (I don't yet know about path
3001 * ellipsis)
3003 uFormat |= lprcFocus ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3005 /* draw the icon */
3006 if (infoPtr->himlNormal != NULL)
3008 if (lvItem.iImage >= 0)
3010 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3011 rcItem.top,
3012 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3013 TRACE("icon %d at (%d,%d)\n",
3014 lvItem.iImage, rcItem.left, rcItem.top);
3018 /* Draw the text below the icon */
3020 /* Don't bother painting item being edited */
3021 if ((infoPtr->bEditing && lprcFocus) || !lvItem.pszText || !lstrlenW(lvItem.pszText))
3023 if(lprcFocus) SetRectEmpty(lprcFocus);
3024 return;
3027 /* Since rcItem.left is left point of icon, compute left point of item box */
3028 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3029 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3030 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3031 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3032 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3033 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3034 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3035 infoPtr->rcList.left, infoPtr->rcList.top,
3036 infoPtr->rcList.right, infoPtr->rcList.bottom,
3037 infoPtr->rcView.left, infoPtr->rcView.top,
3038 infoPtr->rcView.right, infoPtr->rcView.bottom);
3040 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3041 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3044 /* draw label */
3046 /* I am sure of most of the uFormat values. However I am not sure about
3047 * whether we need or do not need the following:
3048 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3049 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3050 * We certainly do not need
3051 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3052 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3055 /* If the text is being drawn without clipping (i.e. the full text) then we
3056 * need to jump through a few hoops to ensure that it all gets displayed and
3057 * that the background is complete
3059 if (uFormat & DT_NOCLIP)
3061 RECT rcBack=rcItem;
3062 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3063 int dx, dy, old_wid, new_wid;
3064 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3065 /* Microsoft, in their great wisdom, have decided that the rectangle
3066 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3067 * not the location. So we have to do the centring ourselves (and take
3068 * responsibility for agreeing off-by-one consistency with them).
3070 old_wid = rcItem.right-rcItem.left;
3071 new_wid = rcBack.right - rcBack.left;
3072 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3073 dy = rcBack.top - rcItem.top;
3074 OffsetRect (&rcItem, dx, dy);
3075 FillRect(hdc, &rcItem, hBrush);
3076 DeleteObject(hBrush);
3078 /* else ? What if we are losing the focus? will we not get a complete
3079 * background?
3081 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3082 TRACE("text at (%d,%d)-(%d,%d) is %s\n",
3083 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3084 debugstr_w(lvItem.pszText));
3086 if(lprcFocus) CopyRect(lprcFocus, &rcItem);
3089 /***
3090 * DESCRIPTION:
3091 * Draws listview items when in report display mode.
3093 * PARAMETER(S):
3094 * [I] infoPtr : valid pointer to the listview structure
3095 * [I] HDC : device context handle
3097 * RETURN:
3098 * None
3100 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3102 SCROLLINFO scrollInfo;
3103 INT nDrawPosY = infoPtr->rcList.top;
3104 INT nColumnCount;
3105 RECT rcItem, rcFull, *lprcFocus;
3106 INT j;
3107 INT nItem;
3108 INT nLast;
3109 BOOL FullSelected;
3110 DWORD cditemmode = CDRF_DODEFAULT;
3111 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
3112 INT scrollOffset;
3114 TRACE("\n");
3116 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3118 /* add 1 for displaying a partial item at the bottom */
3119 nLast = min(nItem + LISTVIEW_GetCountPerColumn(infoPtr) + 1, GETITEMCOUNT(infoPtr));
3121 /* send cache hint notification */
3122 if (lStyle & LVS_OWNERDATA)
3124 NMLVCACHEHINT nmlv;
3126 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
3127 nmlv.hdr.idFrom = GetWindowLongW(infoPtr->hwndSelf,GWL_ID);
3128 nmlv.hdr.code = LVN_ODCACHEHINT;
3129 nmlv.iFrom = nItem;
3130 nmlv.iTo = nLast;
3132 SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3133 (LPARAM)&nmlv);
3136 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3137 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3138 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3140 /* nothing to draw */
3141 if(GETITEMCOUNT(infoPtr) == 0)
3142 return;
3144 /* if we have focus, precompute full select rect, if need be */
3145 if (infoPtr->bFocus && FullSelected)
3147 RECT br;
3149 Header_GetItemRect(infoPtr->hwndHeader, 0, &rcFull);
3150 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3152 rcFull.left += REPORT_MARGINX;
3153 rcFull.right = max(rcFull.left, br.right - REPORT_MARGINX);
3156 /* Get scroll bar info once before loop */
3157 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3158 scrollInfo.cbSize = sizeof(SCROLLINFO);
3159 scrollInfo.fMask = SIF_POS;
3160 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
3161 scrollOffset = scrollInfo.nPos;
3163 for (; nItem < nLast; nItem++)
3165 /* Do Owner Draw */
3166 if (lStyle & LVS_OWNERDRAWFIXED)
3168 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3169 DRAWITEMSTRUCT dis;
3170 LVITEMW item;
3171 RECT br;
3173 TRACE("Owner Drawn\n");
3174 dis.CtlType = ODT_LISTVIEW;
3175 dis.CtlID = uID;
3176 dis.itemID = nItem;
3177 dis.itemAction = ODA_DRAWENTIRE;
3178 dis.itemState = 0;
3180 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
3181 dis.itemState |= ODS_SELECTED;
3182 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
3183 dis.itemState |= ODS_FOCUS;
3185 dis.hwndItem = infoPtr->hwndSelf;
3186 dis.hDC = hdc;
3188 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3190 dis.rcItem.left = -scrollOffset;
3191 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3192 dis.rcItem.top = nDrawPosY;
3193 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3195 ZeroMemory(&item,sizeof(item));
3196 item.iItem = nItem;
3197 item.mask = LVIF_PARAM;
3198 ListView_GetItemW(infoPtr->hwndSelf, &item);
3200 dis.itemData = item.lParam;
3202 if (SendMessageW(GetParent(infoPtr->hwndSelf),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3204 nDrawPosY += infoPtr->nItemHeight;
3205 continue;
3209 /* if we have focus, calculate focus rect */
3210 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
3211 lprcFocus = &infoPtr->rcFocus;
3212 else
3213 lprcFocus = 0;
3215 for (j = 0; j < nColumnCount; j++)
3217 if (cdmode & CDRF_NOTIFYITEMDRAW)
3218 cditemmode = LISTVIEW_SendCustomDrawItemNotify (infoPtr, hdc, nItem, j,
3219 CDDS_ITEMPREPAINT);
3220 if (cditemmode & CDRF_SKIPDEFAULT)
3221 continue;
3223 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3225 rcItem.left += REPORT_MARGINX;
3226 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3227 rcItem.top = nDrawPosY;
3228 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3230 /* Offset the Scroll Bar Pos */
3231 rcItem.left -= scrollOffset;
3232 rcItem.right -= scrollOffset;
3234 if (j == 0)
3235 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem, FullSelected, lprcFocus);
3236 else
3237 LISTVIEW_DrawSubItem(infoPtr, hdc, nItem, j, rcItem, FullSelected);
3239 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3240 LISTVIEW_SendCustomDrawItemNotify(infoPtr, hdc, nItem, 0,
3241 CDDS_ITEMPOSTPAINT);
3244 /* Adjust focus if we have it, and we are in full select */
3245 if (lprcFocus && FullSelected)
3247 rcFull.top = nDrawPosY;
3248 rcFull.bottom = rcFull.top + infoPtr->nItemHeight;
3249 infoPtr->rcFocus = rcFull;
3252 nDrawPosY += infoPtr->nItemHeight;
3256 /***
3257 * DESCRIPTION:
3258 * Draws listview items when in list display mode.
3260 * PARAMETER(S):
3261 * [I] infoPtr : valid pointer to the listview structure
3262 * [I] HDC : device context handle
3264 * RETURN:
3265 * None
3267 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3269 RECT rcItem, *lprcFocus;
3270 INT i, j;
3271 INT nItem;
3272 INT nColumnCount;
3273 INT nCountPerColumn;
3274 INT nItemWidth = infoPtr->nItemWidth;
3275 INT nItemHeight = infoPtr->nItemHeight;
3276 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3277 DWORD cditemmode = CDRF_DODEFAULT;
3279 /* get number of fully visible columns */
3280 nColumnCount = nListWidth / nItemWidth;
3281 if (nListWidth % nItemWidth) nColumnCount++;
3282 infoPtr->nColumnCount = nColumnCount;
3283 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
3284 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3285 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3286 nColumnCount, nCountPerColumn, nItem);
3288 /* nothing to draw, return here */
3289 if(GETITEMCOUNT(infoPtr) == 0)
3290 return;
3292 for (i = 0; i < nColumnCount; i++)
3294 for (j = 0; j < nCountPerColumn; j++, nItem++)
3296 if (nItem >= GETITEMCOUNT(infoPtr))
3297 return;
3299 if (cdmode & CDRF_NOTIFYITEMDRAW)
3300 cditemmode = LISTVIEW_SendCustomDrawItemNotify (infoPtr, hdc, nItem, 0,
3301 CDDS_ITEMPREPAINT);
3302 if (cditemmode & CDRF_SKIPDEFAULT)
3303 continue;
3305 rcItem.top = j * nItemHeight;
3306 rcItem.left = i * nItemWidth;
3307 rcItem.bottom = rcItem.top + nItemHeight;
3308 rcItem.right = rcItem.left + nItemWidth;
3310 /* if we have focus, calculate focus rect */
3311 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
3312 lprcFocus = &infoPtr->rcFocus;
3313 else
3314 lprcFocus = 0;
3316 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem, FALSE, lprcFocus);
3318 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3319 LISTVIEW_SendCustomDrawItemNotify(infoPtr, hdc, nItem, 0,
3320 CDDS_ITEMPOSTPAINT);
3326 /***
3327 * DESCRIPTION:
3328 * Draws listview items when in icon or small icon display mode.
3330 * PARAMETER(S):
3331 * [I] infoPtr : valid pointer to the listview structure
3332 * [I] HDC : device context handle
3334 * RETURN:
3335 * None
3337 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL bSmall, DWORD cdmode)
3339 POINT ptPosition;
3340 POINT ptOrigin;
3341 RECT rcItem, *lprcFocus;
3342 INT i;
3343 DWORD cditemmode = CDRF_DODEFAULT;
3345 TRACE("\n");
3346 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3347 /* DrawItem from erasing the incorrect background area */
3349 /* nothing to draw, return here */
3350 if(GETITEMCOUNT(infoPtr) == 0)
3351 return;
3353 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
3354 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3356 if (cdmode & CDRF_NOTIFYITEMDRAW)
3357 cditemmode = LISTVIEW_SendCustomDrawItemNotify (infoPtr, hdc, i, 0,
3358 CDDS_ITEMPREPAINT);
3359 if (cditemmode & CDRF_SKIPDEFAULT)
3360 continue;
3362 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3363 ptPosition.x += ptOrigin.x;
3364 ptPosition.y += ptOrigin.y;
3366 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3368 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3370 if (ptPosition.y < infoPtr->rcList.bottom)
3372 if (ptPosition.x < infoPtr->rcList.right)
3374 rcItem.top = ptPosition.y;
3375 rcItem.left = ptPosition.x;
3376 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3377 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3379 /* if we have focus, calculate focus rect */
3380 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, i, LVIS_FOCUSED))
3381 lprcFocus = &infoPtr->rcFocus;
3382 else
3383 lprcFocus = 0;
3385 if (bSmall)
3386 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem, FALSE, lprcFocus);
3387 else
3388 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem, lprcFocus);
3393 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3394 LISTVIEW_SendCustomDrawItemNotify(infoPtr, hdc, i, 0,
3395 CDDS_ITEMPOSTPAINT);
3399 /***
3400 * DESCRIPTION:
3401 * Draws listview items.
3403 * PARAMETER(S):
3404 * [I] infoPtr : valid pointer to the listview structure
3405 * [I] HDC : device context handle
3407 * RETURN:
3408 * NoneX
3410 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3412 UINT uView = LISTVIEW_GetType(infoPtr);
3413 HFONT hOldFont;
3414 DWORD cdmode;
3415 RECT rcClient, rcTemp;
3417 LISTVIEW_DUMP(infoPtr);
3419 GetClientRect(infoPtr->hwndSelf, &rcClient);
3421 cdmode = LISTVIEW_SendCustomDrawNotify(infoPtr, CDDS_PREPAINT, hdc, rcClient);
3422 if (cdmode == CDRF_SKIPDEFAULT) return;
3424 infoPtr->bIsDrawing = TRUE;
3426 /* select font */
3427 hOldFont = SelectObject(hdc, infoPtr->hFont);
3429 /* select transparent brush (for drawing the focus box) */
3430 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3432 /* paint the background of the control that doesn't contain any items */
3433 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3434 if (infoPtr->clrBk != CLR_NONE) FillRect(hdc, &rcTemp, infoPtr->hBkBrush);
3436 if (uView == LVS_LIST)
3437 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3438 else if (uView == LVS_REPORT)
3439 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3440 else
3441 LISTVIEW_RefreshIcon(infoPtr, hdc, uView == LVS_SMALLICON, cdmode);
3443 /* if we have a focus rect, draw it */
3444 if (infoPtr->bFocus && !IsRectEmpty(&infoPtr->rcFocus))
3445 DrawFocusRect(hdc, &infoPtr->rcFocus);
3447 /* unselect objects */
3448 SelectObject(hdc, hOldFont);
3450 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3451 LISTVIEW_SendCustomDrawNotify(infoPtr, CDDS_POSTPAINT, hdc, rcClient);
3453 infoPtr->bIsDrawing = FALSE;
3457 /***
3458 * DESCRIPTION:
3459 * Calculates the approximate width and height of a given number of items.
3461 * PARAMETER(S):
3462 * [I] infoPtr : valid pointer to the listview structure
3463 * [I] INT : number of items
3464 * [I] INT : width
3465 * [I] INT : height
3467 * RETURN:
3468 * Returns a DWORD. The width in the low word and the height in high word.
3470 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3471 WORD wWidth, WORD wHeight)
3473 UINT uView = LISTVIEW_GetType(infoPtr);
3474 INT nItemCountPerColumn = 1;
3475 INT nColumnCount = 0;
3476 DWORD dwViewRect = 0;
3478 if (nItemCount == -1)
3479 nItemCount = GETITEMCOUNT(infoPtr);
3481 if (uView == LVS_LIST)
3483 if (wHeight == 0xFFFF)
3485 /* use current height */
3486 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3489 if (wHeight < infoPtr->nItemHeight)
3490 wHeight = infoPtr->nItemHeight;
3492 if (nItemCount > 0)
3494 if (infoPtr->nItemHeight > 0)
3496 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3497 if (nItemCountPerColumn == 0)
3498 nItemCountPerColumn = 1;
3500 if (nItemCount % nItemCountPerColumn != 0)
3501 nColumnCount = nItemCount / nItemCountPerColumn;
3502 else
3503 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3507 /* Microsoft padding magic */
3508 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3509 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3511 dwViewRect = MAKELONG(wWidth, wHeight);
3513 else if (uView == LVS_REPORT)
3514 FIXME("uView == LVS_REPORT: not implemented\n");
3515 else if (uView == LVS_SMALLICON)
3516 FIXME("uView == LVS_SMALLICON: not implemented\n");
3517 else if (uView == LVS_ICON)
3518 FIXME("uView == LVS_ICON: not implemented\n");
3520 return dwViewRect;
3523 /***
3524 * DESCRIPTION:
3525 * Arranges listview items in icon display mode.
3527 * PARAMETER(S):
3528 * [I] infoPtr : valid pointer to the listview structure
3529 * [I] INT : alignment code
3531 * RETURN:
3532 * SUCCESS : TRUE
3533 * FAILURE : FALSE
3535 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3537 UINT uView = LISTVIEW_GetType(infoPtr);
3538 BOOL bResult = FALSE;
3540 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3542 switch (nAlignCode)
3544 case LVA_ALIGNLEFT:
3545 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3546 break;
3547 case LVA_ALIGNTOP:
3548 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3549 break;
3550 case LVA_DEFAULT:
3551 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3552 break;
3553 case LVA_SNAPTOGRID:
3554 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3555 break;
3559 return bResult;
3562 /* << LISTVIEW_CreateDragImage >> */
3565 /***
3566 * DESCRIPTION:
3567 * Removes all listview items and subitems.
3569 * PARAMETER(S):
3570 * [I] infoPtr : valid pointer to the listview structure
3572 * RETURN:
3573 * SUCCESS : TRUE
3574 * FAILURE : FALSE
3576 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3578 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
3579 UINT uView = lStyle & LVS_TYPEMASK;
3580 LISTVIEW_ITEM *lpItem;
3581 LISTVIEW_SUBITEM *lpSubItem;
3582 NMLISTVIEW nmlv;
3583 BOOL bSuppress;
3584 BOOL bResult = FALSE;
3585 HDPA hdpaSubItems;
3587 TRACE("()\n");
3589 LISTVIEW_RemoveAllSelections(infoPtr);
3590 infoPtr->nSelectionMark=-1;
3591 infoPtr->nFocusedItem=-1;
3592 /* But we are supposed to leave nHotItem as is! */
3594 if (lStyle & LVS_OWNERDATA)
3596 infoPtr->hdpaItems->nItemCount = 0;
3597 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
3598 return TRUE;
3601 if (GETITEMCOUNT(infoPtr) > 0)
3603 INT i, j;
3605 /* send LVN_DELETEALLITEMS notification */
3606 /* verify if subsequent LVN_DELETEITEM notifications should be
3607 suppressed */
3608 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3609 nmlv.iItem = -1;
3610 bSuppress = listview_notify(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3612 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3614 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3615 if (hdpaSubItems != NULL)
3617 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3619 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3620 if (lpSubItem != NULL)
3622 /* free subitem string */
3623 if (is_textW(lpSubItem->hdr.pszText))
3624 COMCTL32_Free(lpSubItem->hdr.pszText);
3626 /* free subitem */
3627 COMCTL32_Free(lpSubItem);
3631 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3632 if (lpItem != NULL)
3634 if (!bSuppress)
3636 /* send LVN_DELETEITEM notification */
3637 nmlv.iItem = i;
3638 nmlv.lParam = lpItem->lParam;
3639 listview_notify(infoPtr, LVN_DELETEITEM, &nmlv);
3642 /* free item string */
3643 if (is_textW(lpItem->hdr.pszText))
3644 COMCTL32_Free(lpItem->hdr.pszText);
3646 /* free item */
3647 COMCTL32_Free(lpItem);
3650 DPA_Destroy(hdpaSubItems);
3654 /* reinitialize listview memory */
3655 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3657 /* align items (set position of each item) */
3658 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3660 if (lStyle & LVS_ALIGNLEFT)
3662 LISTVIEW_AlignLeft(infoPtr);
3664 else
3666 LISTVIEW_AlignTop(infoPtr);
3670 LISTVIEW_UpdateScroll(infoPtr);
3672 /* invalidate client area (optimization needed) */
3673 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
3676 return bResult;
3679 /***
3680 * DESCRIPTION:
3681 * Removes a column from the listview control.
3683 * PARAMETER(S):
3684 * [I] infoPtr : valid pointer to the listview structure
3685 * [I] INT : column index
3687 * RETURN:
3688 * SUCCESS : TRUE
3689 * FAILURE : FALSE
3691 static LRESULT LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3693 UINT uView = LISTVIEW_GetType(infoPtr);
3694 UINT uOwnerData = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_OWNERDATA;
3695 BOOL bResult = FALSE;
3697 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3699 bResult = uOwnerData ? TRUE : LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
3701 /* Need to reset the item width when deleting a column */
3702 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
3704 /* reset scroll parameters */
3705 if (uView == LVS_REPORT)
3707 /* update scrollbar(s) */
3708 LISTVIEW_UpdateScroll(infoPtr);
3710 /* refresh client area */
3711 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
3715 return bResult;
3718 /***
3719 * DESCRIPTION:
3720 * Removes an item from the listview control.
3722 * PARAMETER(S):
3723 * [I] infoPtr : valid pointer to the listview structure
3724 * [I] INT : item index
3726 * RETURN:
3727 * SUCCESS : TRUE
3728 * FAILURE : FALSE
3730 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3732 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
3733 UINT uView = lStyle & LVS_TYPEMASK;
3734 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3735 NMLISTVIEW nmlv;
3736 BOOL bResult = FALSE;
3737 HDPA hdpaSubItems;
3738 LISTVIEW_ITEM *lpItem;
3739 LISTVIEW_SUBITEM *lpSubItem;
3740 INT i;
3741 LVITEMW item;
3743 TRACE("(nItem=%d)\n", nItem);
3746 /* First, send LVN_DELETEITEM notification. */
3747 memset(&nmlv, 0, sizeof (NMLISTVIEW));
3748 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
3749 nmlv.hdr.idFrom = lCtrlId;
3750 nmlv.hdr.code = LVN_DELETEITEM;
3751 nmlv.iItem = nItem;
3752 SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
3753 (LPARAM)&nmlv);
3756 /* remove it from the selection range */
3757 ZeroMemory(&item,sizeof(item));
3758 item.stateMask = LVIS_SELECTED;
3759 LISTVIEW_SetItemState(infoPtr,nItem,&item);
3761 if (lStyle & LVS_OWNERDATA)
3763 infoPtr->hdpaItems->nItemCount --;
3764 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
3765 return TRUE;
3768 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
3770 /* initialize memory */
3771 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3773 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
3774 if (hdpaSubItems != NULL)
3776 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3778 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3779 if (lpSubItem != NULL)
3781 /* free item string */
3782 if (is_textW(lpSubItem->hdr.pszText))
3783 COMCTL32_Free(lpSubItem->hdr.pszText);
3785 /* free item */
3786 COMCTL32_Free(lpSubItem);
3790 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3791 if (lpItem != NULL)
3793 /* free item string */
3794 if (is_textW(lpItem->hdr.pszText))
3795 COMCTL32_Free(lpItem->hdr.pszText);
3797 /* free item */
3798 COMCTL32_Free(lpItem);
3801 bResult = DPA_Destroy(hdpaSubItems);
3804 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
3806 /* align items (set position of each item) */
3807 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
3809 if (lStyle & LVS_ALIGNLEFT)
3810 LISTVIEW_AlignLeft(infoPtr);
3811 else
3812 LISTVIEW_AlignTop(infoPtr);
3815 LISTVIEW_UpdateScroll(infoPtr);
3817 /* refresh client area */
3818 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
3821 return bResult;
3825 /***
3826 * DESCRIPTION:
3827 * Callback implementation for editlabel control
3829 * PARAMETER(S):
3830 * [I] infoPtr : valid pointer to the listview structure
3831 * [I] pszText : modified text
3832 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
3834 * RETURN:
3835 * SUCCESS : TRUE
3836 * FAILURE : FALSE
3838 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
3840 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
3841 NMLVDISPINFOW dispInfo;
3842 LISTVIEW_ITEM *lpItem;
3843 HDPA hdpaSubItems;
3844 LISTVIEW_ITEM lvItemRef;
3845 LVITEMW item;
3846 BOOL bResult = TRUE;
3847 INT nItem = infoPtr->nEditLabelItem;
3849 TRACE("(pszText=%s, nItem=%d, isW=%d)\n", debugtext_t(pszText, isW), nItem, isW);
3851 infoPtr->bEditing = FALSE;
3852 if (!(lStyle & LVS_OWNERDATA))
3854 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
3855 return FALSE;
3857 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
3858 return FALSE;
3860 else
3862 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
3863 ZeroMemory(&item,sizeof(item));
3864 item.iItem = nItem;
3865 item.iSubItem = 0;
3866 item.mask = LVIF_PARAM | LVIF_STATE;
3867 ListView_GetItemW(infoPtr->hwndSelf, &item);
3868 lvItemRef.hdr.iImage = item.iImage;
3869 lvItemRef.state = item.state;
3870 lvItemRef.lParam = item.lParam;
3871 lpItem = &lvItemRef;
3874 ZeroMemory(&dispInfo, sizeof(dispInfo));
3875 dispInfo.item.mask = 0;
3876 dispInfo.item.iItem = nItem;
3877 dispInfo.item.state = lpItem->state;
3878 dispInfo.item.stateMask = 0;
3879 dispInfo.item.pszText = pszText;
3880 dispInfo.item.cchTextMax = textlenT(pszText, isW);
3881 dispInfo.item.iImage = lpItem->hdr.iImage;
3882 dispInfo.item.lParam = lpItem->lParam;
3884 /* Do we need to update the Item Text */
3885 if(dispinfo_notifyT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW))
3886 if (lpItem->hdr.pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
3887 bResult = textsetptrT(&lpItem->hdr.pszText, pszText, isW);
3889 return bResult;
3892 /***
3893 * DESCRIPTION:
3894 * Begin in place editing of specified list view item
3896 * PARAMETER(S):
3897 * [I] infoPtr : valid pointer to the listview structure
3898 * [I] INT : item index
3899 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
3901 * RETURN:
3902 * SUCCESS : TRUE
3903 * FAILURE : FALSE
3905 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
3907 NMLVDISPINFOW dispInfo;
3908 RECT rect;
3909 LISTVIEW_ITEM *lpItem;
3910 HWND hedit;
3911 HDPA hdpaSubItems;
3912 WCHAR szDispText[DISP_TEXT_SIZE];
3913 LVITEMW lvItem;
3914 LISTVIEW_ITEM lvItemRef;
3915 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
3917 if (~GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_EDITLABELS)
3918 return FALSE;
3920 infoPtr->nEditLabelItem = nItem;
3922 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
3924 /* Is the EditBox still there, if so remove it */
3925 if(infoPtr->hwndEdit != 0)
3927 SetFocus(infoPtr->hwndSelf);
3928 infoPtr->hwndEdit = 0;
3931 LISTVIEW_SetSelection(infoPtr, nItem);
3932 LISTVIEW_SetItemFocus(infoPtr, nItem);
3934 if (!(lStyle & LVS_OWNERDATA))
3936 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
3937 return 0;
3939 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
3940 return 0;
3942 else
3944 LVITEMW item;
3945 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
3946 ZeroMemory(&item, sizeof(item));
3947 item.iItem = nItem;
3948 item.iSubItem = 0;
3949 item.mask = LVIF_PARAM | LVIF_STATE;
3950 ListView_GetItemW(infoPtr->hwndSelf, &item);
3951 lvItemRef.hdr.iImage = item.iImage;
3952 lvItemRef.state = item.state;
3953 lvItemRef.lParam = item.lParam;
3954 lpItem = &lvItemRef;
3957 /* get information needed for drawing the item */
3958 ZeroMemory(&lvItem, sizeof(lvItem));
3959 lvItem.mask = LVIF_TEXT;
3960 lvItem.iItem = nItem;
3961 lvItem.iSubItem = 0;
3962 lvItem.cchTextMax = DISP_TEXT_SIZE;
3963 lvItem.pszText = szDispText;
3964 *lvItem.pszText = '\0';
3965 LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, isW);
3967 ZeroMemory(&dispInfo, sizeof(dispInfo));
3968 dispInfo.item.mask = 0;
3969 dispInfo.item.iItem = nItem;
3970 dispInfo.item.state = lpItem->state;
3971 dispInfo.item.stateMask = 0;
3972 dispInfo.item.pszText = lvItem.pszText;
3973 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
3974 dispInfo.item.iImage = lpItem->hdr.iImage;
3975 dispInfo.item.lParam = lpItem->lParam;
3977 if (dispinfo_notifyT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
3978 return 0;
3980 rect.left = LVIR_LABEL;
3981 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect))
3982 return 0;
3984 if (!(hedit = CreateEditLabelT(infoPtr, szDispText, WS_VISIBLE,
3985 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW)))
3986 return 0;
3988 infoPtr->hwndEdit = hedit;
3990 ShowWindow(infoPtr->hwndEdit,SW_NORMAL);
3991 infoPtr->bEditing = TRUE;
3992 SetFocus(infoPtr->hwndEdit);
3993 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
3994 return infoPtr->hwndEdit;
3998 /***
3999 * DESCRIPTION:
4000 * Ensures the specified item is visible, scrolling into view if necessary.
4002 * PARAMETER(S):
4003 * [I] infoPtr : valid pointer to the listview structure
4004 * [I] nItem : item index
4005 * [I] bPartial : partially or entirely visible
4007 * RETURN:
4008 * SUCCESS : TRUE
4009 * FAILURE : FALSE
4011 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4013 UINT uView = LISTVIEW_GetType(infoPtr);
4014 INT nScrollPosHeight = 0;
4015 INT nScrollPosWidth = 0;
4016 SCROLLINFO scrollInfo;
4017 RECT rcItem;
4018 BOOL bRedraw = FALSE;
4020 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4021 scrollInfo.cbSize = sizeof(SCROLLINFO);
4022 scrollInfo.fMask = SIF_POS;
4024 /* ALWAYS bPartial == FALSE, FOR NOW! */
4026 rcItem.left = LVIR_BOUNDS;
4027 if (LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem))
4029 if (rcItem.left < infoPtr->rcList.left)
4031 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4033 /* scroll left */
4034 bRedraw = TRUE;
4035 if (uView == LVS_LIST)
4037 nScrollPosWidth = infoPtr->nItemWidth;
4038 rcItem.left += infoPtr->rcList.left;
4040 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4042 nScrollPosWidth = 1;
4043 rcItem.left += infoPtr->rcList.left;
4046 /* When in LVS_REPORT view, the scroll position should
4047 not be updated. */
4048 if (nScrollPosWidth != 0)
4050 if (rcItem.left % nScrollPosWidth == 0)
4051 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4052 else
4053 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4055 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
4059 else if (rcItem.right > infoPtr->rcList.right)
4061 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4063 /* scroll right */
4064 bRedraw = TRUE;
4065 if (uView == LVS_LIST)
4067 rcItem.right -= infoPtr->rcList.right;
4068 nScrollPosWidth = infoPtr->nItemWidth;
4070 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4072 rcItem.right -= infoPtr->rcList.right;
4073 nScrollPosWidth = 1;
4076 /* When in LVS_REPORT view, the scroll position should
4077 not be updated. */
4078 if (nScrollPosWidth != 0)
4080 if (rcItem.right % nScrollPosWidth == 0)
4081 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4082 else
4083 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4085 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
4090 if (rcItem.top < infoPtr->rcList.top)
4092 /* scroll up */
4093 bRedraw = TRUE;
4094 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4096 if (uView == LVS_REPORT)
4098 rcItem.top -= infoPtr->rcList.top;
4099 nScrollPosHeight = infoPtr->nItemHeight;
4101 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4103 nScrollPosHeight = 1;
4104 rcItem.top += infoPtr->rcList.top;
4107 if (rcItem.top % nScrollPosHeight == 0)
4108 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4109 else
4110 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4112 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
4115 else if (rcItem.bottom > infoPtr->rcList.bottom)
4117 /* scroll down */
4118 bRedraw = TRUE;
4119 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4121 if (uView == LVS_REPORT)
4123 rcItem.bottom -= infoPtr->rcList.bottom;
4124 nScrollPosHeight = infoPtr->nItemHeight;
4126 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4128 nScrollPosHeight = 1;
4129 rcItem.bottom -= infoPtr->rcList.bottom;
4132 if (rcItem.bottom % nScrollPosHeight == 0)
4133 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4134 else
4135 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4137 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
4142 if(bRedraw)
4143 InvalidateRect(infoPtr->hwndSelf,NULL,TRUE);
4144 return TRUE;
4147 /***
4148 * DESCRIPTION:
4149 * Retrieves the nearest item, given a position and a direction.
4151 * PARAMETER(S):
4152 * [I] infoPtr : valid pointer to the listview structure
4153 * [I] POINT : start position
4154 * [I] UINT : direction
4156 * RETURN:
4157 * Item index if successdful, -1 otherwise.
4159 static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDirection)
4161 LV_INTHIT lvIntHit;
4162 INT nItem = -1;
4163 RECT rcView;
4165 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4166 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4167 ((vkDirection == VK_UP) ? "VK_UP" :
4168 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4170 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
4172 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4173 LISTVIEW_GetOrigin(infoPtr, &lvIntHit.ht.pt);
4174 lvIntHit.ht.pt.x += pt.x;
4175 lvIntHit.ht.pt.y += pt.y;
4177 if (vkDirection == VK_DOWN)
4178 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4179 else if (vkDirection == VK_UP)
4180 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4181 else if (vkDirection == VK_LEFT)
4182 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4183 else if (vkDirection == VK_RIGHT)
4184 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4186 if (!PtInRect(&rcView, lvIntHit.ht.pt))
4187 return -1;
4188 else
4190 nItem = LISTVIEW_SuperHitTestItem(infoPtr, &lvIntHit, TRUE);
4191 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4195 return nItem;
4198 /***
4199 * DESCRIPTION:
4200 * Searches for an item with specific characteristics.
4202 * PARAMETER(S):
4203 * [I] hwnd : window handle
4204 * [I] nStart : base item index
4205 * [I] lpFindInfo : item information to look for
4207 * RETURN:
4208 * SUCCESS : index of item
4209 * FAILURE : -1
4211 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4212 LPLVFINDINFOW lpFindInfo)
4214 POINT ptItem;
4215 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4216 LVITEMW lvItem;
4217 BOOL bWrap = FALSE;
4218 INT nItem = nStart;
4219 INT nLast = GETITEMCOUNT(infoPtr);
4221 if ((nItem >= -1) && (lpFindInfo != NULL))
4223 ZeroMemory(&lvItem, sizeof(lvItem));
4225 if (lpFindInfo->flags & LVFI_PARAM)
4227 lvItem.mask |= LVIF_PARAM;
4230 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4232 lvItem.mask |= LVIF_TEXT;
4233 lvItem.pszText = szDispText;
4234 lvItem.cchTextMax = DISP_TEXT_SIZE;
4237 if (lpFindInfo->flags & LVFI_WRAP)
4238 bWrap = TRUE;
4240 if (lpFindInfo->flags & LVFI_NEARESTXY)
4242 ptItem.x = lpFindInfo->pt.x;
4243 ptItem.y = lpFindInfo->pt.y;
4246 while (1)
4248 while (nItem < nLast)
4250 if (lpFindInfo->flags & LVFI_NEARESTXY)
4252 nItem = LISTVIEW_GetNearestItem(infoPtr, ptItem,
4253 lpFindInfo->vkDirection);
4254 if (nItem != -1)
4256 /* get position of the new item index */
4257 if (!ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &ptItem))
4258 return -1;
4260 else
4261 return -1;
4263 else
4265 nItem++;
4268 lvItem.iItem = nItem;
4269 lvItem.iSubItem = 0;
4270 if (LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE))
4272 if (lvItem.mask & LVIF_TEXT)
4274 if (lpFindInfo->flags & LVFI_PARTIAL)
4276 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4277 continue;
4279 else
4281 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4282 continue;
4286 if (lvItem.mask & LVIF_PARAM)
4288 if (lpFindInfo->lParam != lvItem.lParam)
4289 continue;
4292 return nItem;
4296 if (bWrap)
4298 nItem = -1;
4299 nLast = nStart + 1;
4300 bWrap = FALSE;
4302 else
4304 return -1;
4309 return -1;
4312 /***
4313 * DESCRIPTION:
4314 * Searches for an item with specific characteristics.
4316 * PARAMETER(S):
4317 * [I] hwnd : window handle
4318 * [I] nStart : base item index
4319 * [I] lpFindInfo : item information to look for
4321 * RETURN:
4322 * SUCCESS : index of item
4323 * FAILURE : -1
4325 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4326 LPLVFINDINFOA lpFindInfo)
4328 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4329 LVFINDINFOW fiw;
4330 LRESULT res;
4332 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4333 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4334 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4335 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4336 return res;
4339 /***
4340 * DESCRIPTION:
4341 * Retrieves the background image of the listview control.
4343 * PARAMETER(S):
4344 * [I] infoPtr : valid pointer to the listview structure
4345 * [O] LPLVMKBIMAGE : background image attributes
4347 * RETURN:
4348 * SUCCESS : TRUE
4349 * FAILURE : FALSE
4351 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4352 /* { */
4353 /* FIXME (listview, "empty stub!\n"); */
4354 /* return FALSE; */
4355 /* } */
4357 /***
4358 * DESCRIPTION:
4359 * Retrieves column attributes.
4361 * PARAMETER(S):
4362 * [I] infoPtr : valid pointer to the listview structure
4363 * [I] INT : column index
4364 * [IO] LPLVCOLUMNW : column information
4365 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4366 * otherwise it is in fact a LPLVCOLUMNA
4368 * RETURN:
4369 * SUCCESS : TRUE
4370 * FAILURE : FALSE
4372 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4374 HDITEMW hdi;
4375 BOOL bResult = FALSE;
4377 if (lpColumn != NULL)
4380 /* initialize memory */
4381 ZeroMemory(&hdi, sizeof(hdi));
4383 if (lpColumn->mask & LVCF_FMT)
4384 hdi.mask |= HDI_FORMAT;
4386 if (lpColumn->mask & LVCF_WIDTH)
4387 hdi.mask |= HDI_WIDTH;
4389 if (lpColumn->mask & LVCF_TEXT)
4391 hdi.mask |= HDI_TEXT;
4392 hdi.cchTextMax = lpColumn->cchTextMax;
4393 hdi.pszText = lpColumn->pszText;
4396 if (lpColumn->mask & LVCF_IMAGE)
4397 hdi.mask |= HDI_IMAGE;
4399 if (lpColumn->mask & LVCF_ORDER)
4400 hdi.mask |= HDI_ORDER;
4402 if (isW)
4403 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4404 else
4405 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4407 if (bResult)
4409 if (lpColumn->mask & LVCF_FMT)
4411 lpColumn->fmt = 0;
4413 if (hdi.fmt & HDF_LEFT)
4414 lpColumn->fmt |= LVCFMT_LEFT;
4415 else if (hdi.fmt & HDF_RIGHT)
4416 lpColumn->fmt |= LVCFMT_RIGHT;
4417 else if (hdi.fmt & HDF_CENTER)
4418 lpColumn->fmt |= LVCFMT_CENTER;
4420 if (hdi.fmt & HDF_IMAGE)
4421 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4423 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4424 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4427 if (lpColumn->mask & LVCF_WIDTH)
4428 lpColumn->cx = hdi.cxy;
4430 if (lpColumn->mask & LVCF_IMAGE)
4431 lpColumn->iImage = hdi.iImage;
4433 if (lpColumn->mask & LVCF_ORDER)
4434 lpColumn->iOrder = hdi.iOrder;
4436 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4437 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4442 return bResult;
4446 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4448 INT i;
4450 if (!lpiArray)
4451 return FALSE;
4453 /* FIXME: little hack */
4454 for (i = 0; i < iCount; i++)
4455 lpiArray[i] = i;
4457 return TRUE;
4460 /***
4461 * DESCRIPTION:
4462 * Retrieves the column width.
4464 * PARAMETER(S):
4465 * [I] infoPtr : valid pointer to the listview structure
4466 * [I] int : column index
4468 * RETURN:
4469 * SUCCESS : column width
4470 * FAILURE : zero
4472 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4474 UINT uView = LISTVIEW_GetType(infoPtr);
4475 INT nColumnWidth = 0;
4476 HDITEMW hdi;
4478 if (uView == LVS_LIST)
4480 nColumnWidth = infoPtr->nItemWidth;
4482 else if (uView == LVS_REPORT)
4484 /* get column width from header */
4485 ZeroMemory(&hdi, sizeof(hdi));
4486 hdi.mask = HDI_WIDTH;
4487 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4488 nColumnWidth = hdi.cxy;
4491 return nColumnWidth;
4494 /***
4495 * DESCRIPTION:
4496 * In list or report display mode, retrieves the number of items that can fit
4497 * vertically in the visible area. In icon or small icon display mode,
4498 * retrieves the total number of visible items.
4500 * PARAMETER(S):
4501 * [I] infoPtr : valid pointer to the listview structure
4503 * RETURN:
4504 * Number of fully visible items.
4506 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4508 UINT uView = LISTVIEW_GetType(infoPtr);
4509 INT nItemCount = 0;
4511 if (uView == LVS_LIST)
4513 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4515 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4516 LISTVIEW_GetCountPerColumn(infoPtr);
4519 else if (uView == LVS_REPORT)
4521 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4523 else
4525 nItemCount = GETITEMCOUNT(infoPtr);
4528 return nItemCount;
4532 /***
4533 * DESCRIPTION:
4534 * Retrieves an image list handle.
4536 * PARAMETER(S):
4537 * [I] infoPtr : valid pointer to the listview structure
4538 * [I] INT : image list identifier
4540 * RETURN:
4541 * SUCCESS : image list handle
4542 * FAILURE : NULL
4544 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4546 HIMAGELIST himl = NULL;
4548 switch (nImageList)
4550 case LVSIL_NORMAL:
4551 himl = infoPtr->himlNormal;
4552 break;
4553 case LVSIL_SMALL:
4554 himl = infoPtr->himlSmall;
4555 break;
4556 case LVSIL_STATE:
4557 himl = infoPtr->himlState;
4558 break;
4561 return (LRESULT)himl;
4564 /* LISTVIEW_GetISearchString */
4566 /***
4567 * Helper function for LISTVIEW_GetItemT *only*. Tests if an item is selected.
4568 * It is important that no other functions call this because of callbacks.
4570 static inline BOOL is_item_selected(LISTVIEW_INFO *infoPtr, INT nItem)
4572 LISTVIEW_SELECTION selection = { nItem, nItem };
4574 return DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
4575 LISTVIEW_CompareSelectionRanges, 0, DPAS_SORTED) != -1;
4578 /***
4579 * DESCRIPTION:
4580 * Retrieves item attributes.
4582 * PARAMETER(S):
4583 * [I] hwnd : window handle
4584 * [IO] lpLVItem : item info
4585 * [I] internal : if true then we will use tricks that avoid copies
4586 * but are not compatible with the regular interface
4587 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4588 * if FALSE, the lpLVItem is a LPLVITEMA.
4590 * RETURN:
4591 * SUCCESS : TRUE
4592 * FAILURE : FALSE
4594 static LRESULT LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
4596 NMLVDISPINFOW dispInfo;
4597 LISTVIEW_ITEM *lpItem;
4598 ITEMHDR* pItemHdr;
4599 HDPA hdpaSubItems;
4601 if (internal && !isW)
4603 ERR("We can't have internal non-Unicode GetItem!\n");
4604 return FALSE;
4607 /* In the following:
4608 * lpLVItem describes the information requested by the user
4609 * lpItem is what we have
4610 * dispInfo is a structure we use to request the missing
4611 * information from the application
4614 TRACE("(lpLVItem=%s, internal=%d, isW=%d)\n",
4615 debuglvitem_t(lpLVItem, isW), internal, isW);
4617 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4618 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
4619 return FALSE;
4621 /* a quick optimization if all we're asked is the focus state
4622 * these queries are worth optimising since they are common,
4623 * and can be answered in constant time, without the heavy accesses */
4624 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4625 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4627 lpLVItem->state = 0;
4628 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4629 lpLVItem->state |= LVIS_FOCUSED;
4630 return TRUE;
4633 ZeroMemory(&dispInfo, sizeof(dispInfo));
4635 /* if the app stores all the data, handle it separately */
4636 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_OWNERDATA)
4638 dispInfo.item.state = 0;
4640 /* if we need to callback, do it now */
4641 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4643 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
4644 dispInfo.item.stateMask &= infoPtr->uCallbackMask;
4645 dispinfo_notifyT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4646 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
4647 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4650 /* we store only a little state, so if we're not asked, we're done */
4651 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return FALSE;
4653 /* if focus is handled by us, report it */
4654 if ( !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4656 lpLVItem->state &= ~LVIS_FOCUSED;
4657 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4658 lpLVItem->state |= LVIS_FOCUSED;
4661 /* and do the same for selection, if we handle it */
4662 if ( !(infoPtr->uCallbackMask & LVIS_SELECTED) )
4664 lpLVItem->state &= ~LVIS_SELECTED;
4665 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
4666 is_item_selected(infoPtr, lpLVItem->iItem))
4667 lpLVItem->state |= LVIS_SELECTED;
4670 return TRUE;
4673 /* find the item and subitem structures before we proceed */
4674 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4675 if (hdpaSubItems == NULL) return FALSE;
4677 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4678 return FALSE;
4680 if (lpLVItem->iSubItem)
4682 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4683 if(!lpSubItem) return FALSE;
4684 pItemHdr = &lpSubItem->hdr;
4686 else
4687 pItemHdr = &lpItem->hdr;
4689 /* Do we need to query the state from the app? */
4690 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4692 dispInfo.item.mask |= LVIF_STATE;
4693 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4696 /* Do we need to enquire about the image? */
4697 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4698 dispInfo.item.mask |= LVIF_IMAGE;
4700 /* Do we need to enquire about the text? */
4701 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4703 dispInfo.item.mask |= LVIF_TEXT;
4704 dispInfo.item.pszText = lpLVItem->pszText;
4705 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4706 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4707 *dispInfo.item.pszText = '\0';
4710 /* If we don't have all the requested info, query the application */
4711 if (dispInfo.item.mask != 0)
4713 dispInfo.item.iItem = lpLVItem->iItem;
4714 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4715 dispInfo.item.lParam = lpItem->lParam;
4716 dispinfo_notifyT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4717 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4720 /* Now, handle the iImage field */
4721 if (dispInfo.item.mask & LVIF_IMAGE)
4723 lpLVItem->iImage = dispInfo.item.iImage;
4724 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4725 pItemHdr->iImage = dispInfo.item.iImage;
4727 else if (lpLVItem->mask & LVIF_IMAGE)
4728 lpLVItem->iImage = pItemHdr->iImage;
4730 /* The pszText field */
4731 if (dispInfo.item.mask & LVIF_TEXT)
4733 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4734 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4736 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
4737 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
4738 if (lpLVItem->pszText != dispInfo.item.pszText)
4739 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
4741 else if (lpLVItem->mask & LVIF_TEXT)
4743 if (internal) lpLVItem->pszText = pItemHdr->pszText;
4744 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4747 /* if this is a subitem, we're done*/
4748 if (lpLVItem->iSubItem) return TRUE;
4750 /* Next is the lParam field */
4751 if (dispInfo.item.mask & LVIF_PARAM)
4753 lpLVItem->lParam = dispInfo.item.lParam;
4754 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4755 lpItem->lParam = dispInfo.item.lParam;
4757 else if (lpLVItem->mask & LVIF_PARAM)
4758 lpLVItem->lParam = lpItem->lParam;
4760 /* ... the state field (this one is different due to uCallbackmask) */
4761 if (lpLVItem->mask & LVIF_STATE)
4763 lpLVItem->state = lpItem->state;
4764 if (dispInfo.item.mask & LVIF_STATE)
4766 lpLVItem->state &= ~dispInfo.item.stateMask;
4767 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4769 if ( !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4771 lpLVItem->state &= ~LVIS_FOCUSED;
4772 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4773 lpLVItem->state |= LVIS_FOCUSED;
4775 if ( !(infoPtr->uCallbackMask & LVIS_SELECTED) )
4777 lpLVItem->state &= ~LVIS_SELECTED;
4778 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
4779 is_item_selected(infoPtr, lpLVItem->iItem))
4780 lpLVItem->state |= LVIS_SELECTED;
4784 /* and last, but not least, the indent field */
4785 if (lpLVItem->mask & LVIF_INDENT)
4786 lpLVItem->iIndent = lpItem->iIndent;
4788 return TRUE;
4792 /***
4793 * DESCRIPTION:
4794 * Retrieves the rectangle enclosing the item icon and text.
4796 * PARAMETER(S):
4797 * [I] infoPtr : valid pointer to the listview structure
4798 * [I] INT : item index
4799 * [O] LPRECT : coordinate information
4801 * RETURN:
4802 * SUCCESS : TRUE
4803 * FAILURE : FALSE
4805 static BOOL LISTVIEW_GetItemBoundBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lpRect)
4807 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
4808 UINT uView = lStyle & LVS_TYPEMASK;
4809 BOOL bResult = FALSE;
4810 HDPA hdpaSubItems;
4811 LISTVIEW_ITEM *lpItem;
4812 INT nCountPerColumn;
4813 INT nRow;
4815 TRACE("(nItem=%d,lpRect=%p)\n", nItem, lpRect);
4817 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
4818 (lpRect != NULL))
4820 if (uView == LVS_LIST)
4822 bResult = TRUE;
4823 nItem = nItem - ListView_GetTopIndex(infoPtr->hwndSelf);
4824 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
4825 if (nItem < 0)
4827 nRow = nItem % nCountPerColumn;
4828 if (nRow == 0)
4830 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
4831 lpRect->top = 0;
4833 else
4835 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
4836 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
4839 else
4841 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
4842 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
4845 else if (uView == LVS_REPORT)
4847 bResult = TRUE;
4848 lpRect->left = REPORT_MARGINX;
4849 lpRect->top = ((nItem - ListView_GetTopIndex(infoPtr->hwndSelf)) *
4850 infoPtr->nItemHeight) + infoPtr->rcList.top;
4852 if (!(lStyle & LVS_NOSCROLL))
4854 SCROLLINFO scrollInfo;
4855 /* Adjust position by scrollbar offset */
4856 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4857 scrollInfo.cbSize = sizeof(SCROLLINFO);
4858 scrollInfo.fMask = SIF_POS;
4859 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
4860 lpRect->left -= scrollInfo.nPos;
4863 else /* either LVS_ICON or LVS_SMALLICON */
4865 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4867 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4869 bResult = TRUE;
4870 lpRect->left = lpItem->ptPosition.x;
4871 lpRect->top = lpItem->ptPosition.y;
4876 lpRect->right = lpRect->left + infoPtr->nItemWidth;
4877 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
4878 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
4879 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
4880 return bResult;
4883 /***
4884 * DESCRIPTION:
4885 * Retrieves the position (upper-left) of the listview control item.
4886 * Note that for LVS_ICON style, the upper-left is that of the icon
4887 * and not the bounding box.
4889 * PARAMETER(S):
4890 * [I] infoPtr : valid pointer to the listview structure
4891 * [I] INT : item index
4892 * [O] LPPOINT : coordinate information
4894 * RETURN:
4895 * SUCCESS : TRUE
4896 * FAILURE : FALSE
4898 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4900 UINT uView = LISTVIEW_GetType(infoPtr);
4901 BOOL bResult = FALSE;
4902 RECT rcBounding;
4904 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4906 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
4907 (lpptPosition != NULL))
4909 bResult = LISTVIEW_GetItemBoundBox(infoPtr, nItem, &rcBounding);
4910 lpptPosition->x = rcBounding.left;
4911 lpptPosition->y = rcBounding.top;
4912 if (uView == LVS_ICON)
4914 lpptPosition->y += ICON_TOP_PADDING;
4915 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
4917 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
4918 lpptPosition->x, lpptPosition->y);
4920 return bResult;
4923 /***
4924 * Adjust a text rectangle to an integral number of text lines.
4926 static void LISTVIEW_GetIntegralLines(
4927 const LISTVIEW_INFO *infoPtr,
4928 RECT *rcText)
4930 INT i, j, k, l;
4933 * We need to have the bottom to be an intergal number of
4934 * text lines (ntmHeight) below text top that is less than
4935 * or equal to the nItemHeight.
4937 i = infoPtr->nItemHeight - infoPtr->iconSize.cy -
4938 ICON_TOP_PADDING - ICON_BOTTOM_PADDING;
4939 j = i / infoPtr->ntmHeight;
4940 k = j * infoPtr->ntmHeight;
4941 l = rcText->top + k;
4942 rcText->bottom = min(rcText->bottom, l);
4943 rcText->bottom += 1;
4945 TRACE("integral lines, nitemH=%d, ntmH=%d, icon.cy=%ld, i=%d, j=%d, k=%d, rect=(%d,%d)-(%d,%d)\n",
4946 infoPtr->nItemHeight, infoPtr->ntmHeight, infoPtr->iconSize.cy,
4947 i, j, k,
4948 rcText->left, rcText->top, rcText->right, rcText->bottom);
4952 /***
4953 * DESCRIPTION: [INTERNAL]
4954 * Update the bounding rectangle around the text under a large icon.
4955 * This depends on whether it has the focus or not.
4956 * On entry the rectangle's top, left and right should be set.
4957 * On return the bottom will also be set and the width may have been
4958 * modified.
4960 * PARAMETER
4961 * [I] infoPtr : pointer to the listview structure
4962 * [I] nItem : the item for which we are calculating this
4963 * [I] rect : the rectangle to be updated
4965 * This appears to be weird, even in the Microsoft implementation.
4967 static void LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *infoPtr, int nItem, RECT *rect)
4969 HDC hdc = GetDC (infoPtr->hwndSelf);
4970 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
4971 UINT uFormat = LISTVIEW_DTFLAGS | DT_CALCRECT;
4972 RECT rcText = *rect;
4973 RECT rcBack = *rect;
4974 BOOL focused, selected;
4975 int dx, dy, old_wid, new_wid;
4977 TRACE("%s, focus item=%d, cur item=%d\n",
4978 (infoPtr->bFocus) ? "Window has focus" : "Window not focused",
4979 infoPtr->nFocusedItem, nItem);
4982 focused = infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED);
4983 selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4985 uFormat |= (focused) ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
4987 if (focused || selected)
4989 /* We (aim to) display the full text. In Windows 95 it appears to
4990 * calculate the size assuming the specified font and then it draws
4991 * the text in that region with the specified font except scaled to
4992 * 10 point (or the height of the system font or ...). Thus if the
4993 * window has 24 point Helvetica the highlit rectangle will be
4994 * taller than the text and if it is 7 point Helvetica then the text
4995 * will be clipped.
4996 * For now we will simply say that it is the correct size to display
4997 * the text in the specified font.
4999 LVITEMW lvItem;
5000 lvItem.mask = LVIF_TEXT;
5001 lvItem.iItem = nItem;
5002 lvItem.iSubItem = 0;
5003 /* We will specify INTERNAL and so will receive back a const
5004 * pointer to the text, rather than specifying a buffer to which
5005 * to copy it.
5007 LISTVIEW_GetItemW (infoPtr, &lvItem, TRUE);
5009 InflateRect(&rcText, -2, 0);
5010 DrawTextW (hdc, lvItem.pszText, -1, &rcText, uFormat);
5011 /* Microsoft, in their great wisdom, have decided that the rectangle
5012 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
5013 * not the location. So we have to do the centring ourselves (and take
5014 * responsibility for agreeing off-by-one consistency with them).
5017 old_wid = rcText.right - rcText.left;
5018 new_wid = rcBack.right - rcBack.left;
5019 dx = rcBack.left - rcText.left + (new_wid-old_wid)/2;
5020 dy = rcBack.top - rcText.top;
5021 OffsetRect (&rcText, dx, dy);
5023 if (!focused)
5025 LISTVIEW_GetIntegralLines(infoPtr, &rcText);
5027 else
5029 rcText.bottom += LABEL_VERT_PADDING - 2;
5031 *rect = rcBack;
5032 rect->bottom = rcText.bottom;
5034 else
5036 /* As far as I can see the text region seems to be trying to be
5037 * "tall enough for two lines of text". Once again (comctl32.dll ver
5038 * 5.81?) it measures this on the basis of the selected font and then
5039 * draws it with the same font except in 10 point size. This can lead
5040 * to more or less than the two rows appearing.
5041 * Question; are we supposed to be including DT_EXTERNALLEADING?
5042 * Question; should the width be shrunk to the space required to
5043 * display the two lines?
5045 LISTVIEW_GetIntegralLines(infoPtr, &rcText);
5046 rect->bottom = rcText.bottom;
5049 TRACE("%s and %s, bounding rect=(%d,%d)-(%d,%d)\n",
5050 (focused) ? "focused(full text)" : "not focused",
5051 (selected) ? "selected" : "not selected",
5052 rect->left, rect->top, rect->right, rect->bottom);
5054 SelectObject (hdc, hOldFont);
5055 ReleaseDC (infoPtr->hwndSelf, hdc);
5058 /***
5059 * DESCRIPTION:
5060 * Retrieves the bounding rectangle for a listview control item.
5062 * PARAMETER(S):
5063 * [I] infoPtr : valid pointer to the listview structure
5064 * [I] INT : item index
5065 * [IO] LPRECT : bounding rectangle coordinates
5066 * lprc->left specifies the portion of the item for which the bounding
5067 * rectangle will be retrieved.
5069 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5070 * including the icon and label.
5072 * * For LVS_ICON
5073 * * Experiment shows that native control returns:
5074 * * width = min (48, length of text line)
5075 * * .left = position.x - (width - iconsize.cx)/2
5076 * * .right = .left + width
5077 * * height = #lines of text * ntmHeight + icon height + 8
5078 * * .top = position.y - 2
5079 * * .bottom = .top + height
5080 * * separation between items .y = itemSpacing.cy - height
5081 * * .x = itemSpacing.cx - width
5082 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5084 * * For LVS_ICON
5085 * * Experiment shows that native control returns:
5086 * * width = iconSize.cx + 16
5087 * * .left = position.x - (width - iconsize.cx)/2
5088 * * .right = .left + width
5089 * * height = iconSize.cy + 4
5090 * * .top = position.y - 2
5091 * * .bottom = .top + height
5092 * * separation between items .y = itemSpacing.cy - height
5093 * * .x = itemSpacing.cx - width
5094 * LVIR_LABEL Returns the bounding rectangle of the item text.
5096 * * For LVS_ICON
5097 * * Experiment shows that native control returns:
5098 * * width = text length
5099 * * .left = position.x - width/2
5100 * * .right = .left + width
5101 * * height = ntmH * linecount + 2
5102 * * .top = position.y + iconSize.cy + 6
5103 * * .bottom = .top + height
5104 * * separation between items .y = itemSpacing.cy - height
5105 * * .x = itemSpacing.cx - width
5106 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5107 * rectangles, but excludes columns in report view.
5109 * RETURN:
5110 * SUCCESS : TRUE
5111 * FAILURE : FALSE
5113 * NOTES
5114 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5115 * upon whether the window has the focus currently and on whether the item
5116 * is the one with the focus. Ensure that the control's record of which
5117 * item has the focus agrees with the items' records.
5119 static LRESULT LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5121 UINT uView = LISTVIEW_GetType(infoPtr);
5122 BOOL bResult = FALSE;
5123 POINT ptOrigin;
5124 POINT ptItem;
5125 INT nLeftPos;
5126 INT nLabelWidth;
5127 INT nIndent;
5128 LVITEMW lvItem;
5129 RECT rcInternal;
5131 TRACE("(hwnd=%x, nItem=%d, lprc=%p, uview=%d)\n",
5132 infoPtr->hwndSelf, nItem, lprc, uView);
5134 if (uView & LVS_REPORT)
5136 ZeroMemory(&lvItem, sizeof(lvItem));
5137 lvItem.mask = LVIF_INDENT;
5138 lvItem.iItem = nItem;
5139 lvItem.iSubItem = 0;
5140 LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE);
5142 /* do indent */
5143 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5144 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5145 else
5146 nIndent = 0;
5148 else
5149 nIndent = 0;
5151 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5153 switch(lprc->left)
5155 case LVIR_ICON:
5156 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptItem)) break;
5157 if (uView == LVS_ICON)
5159 if (infoPtr->himlNormal != NULL)
5161 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5163 bResult = TRUE;
5164 lprc->left = ptItem.x + ptOrigin.x - 8;
5165 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING;
5166 lprc->right = lprc->left + infoPtr->iconSize.cx + 16;
5167 lprc->bottom = lprc->top + infoPtr->iconSize.cy +
5168 ICON_TOP_PADDING;
5172 else if (uView == LVS_SMALLICON)
5174 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5176 bResult = TRUE;
5177 lprc->left = ptItem.x + ptOrigin.x;
5178 lprc->top = ptItem.y + ptOrigin.y;
5179 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5181 if (infoPtr->himlState != NULL)
5182 lprc->left += infoPtr->iconSize.cx;
5184 if (infoPtr->himlSmall != NULL)
5185 lprc->right = lprc->left + infoPtr->iconSize.cx;
5186 else
5187 lprc->right = lprc->left;
5190 else
5192 bResult = TRUE;
5193 lprc->left = ptItem.x;
5194 if (uView & LVS_REPORT)
5195 lprc->left += nIndent;
5196 lprc->top = ptItem.y;
5197 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5199 if (infoPtr->himlState != NULL)
5200 lprc->left += infoPtr->iconSize.cx;
5202 if (infoPtr->himlSmall != NULL)
5203 lprc->right = lprc->left + infoPtr->iconSize.cx;
5204 else
5205 lprc->right = lprc->left;
5207 break;
5209 case LVIR_LABEL:
5210 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptItem)) break;
5211 if (uView == LVS_ICON)
5213 if (infoPtr->himlNormal != NULL)
5215 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5217 bResult = TRUE;
5219 /* Correct ptItem to icon upper-left */
5220 ptItem.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5221 ptItem.y -= ICON_TOP_PADDING;
5223 lprc->left = ptItem.x + ptOrigin.x;
5224 lprc->top = ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5226 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5227 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5229 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5230 lprc->right = lprc->left + nLabelWidth;
5231 lprc->bottom = lprc->top + infoPtr->ntmHeight + 1;
5232 InflateRect(lprc, 2, 0);
5234 else
5236 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5237 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5238 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, lprc);
5240 lprc->bottom += HEIGHT_PADDING;
5244 else if (uView == LVS_SMALLICON)
5246 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5248 bResult = TRUE;
5249 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5250 lprc->top = ptItem.y + ptOrigin.y;
5251 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5253 if (infoPtr->himlState != NULL)
5254 lprc->left += infoPtr->iconSize.cx;
5256 if (infoPtr->himlSmall != NULL)
5257 lprc->left += infoPtr->iconSize.cx;
5259 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5260 nLabelWidth += TRAILING_PADDING;
5261 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5262 lprc->right = lprc->left + nLabelWidth;
5263 else
5264 lprc->right = nLeftPos + infoPtr->nItemWidth;
5267 else
5269 bResult = TRUE;
5270 if (uView == LVS_REPORT)
5271 nLeftPos = lprc->left = ptItem.x + nIndent;
5272 else
5273 nLeftPos = lprc->left = ptItem.x;
5274 lprc->top = ptItem.y;
5275 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5277 if (infoPtr->himlState != NULL)
5278 lprc->left += infoPtr->iconSize.cx;
5280 if (infoPtr->himlSmall != NULL)
5281 lprc->left += infoPtr->iconSize.cx;
5283 if (uView != LVS_REPORT)
5285 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5286 nLabelWidth += TRAILING_PADDING;
5287 if (infoPtr->himlSmall)
5288 nLabelWidth += IMAGE_PADDING;
5290 else
5291 nLabelWidth = LISTVIEW_GetColumnWidth(infoPtr, 0)-lprc->left;
5292 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5293 lprc->right = lprc->left + nLabelWidth;
5294 else
5295 lprc->right = nLeftPos + infoPtr->nItemWidth;
5297 break;
5299 case LVIR_BOUNDS:
5300 if (!LISTVIEW_GetItemBoundBox(infoPtr, nItem, &rcInternal)) break;
5301 ptItem.x = rcInternal.left;
5302 ptItem.y = rcInternal.top;
5303 if (uView == LVS_ICON)
5305 if (infoPtr->himlNormal != NULL)
5307 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5309 RECT label_rect, icon_rect;
5311 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptItem)) break;
5313 /* make icon rectangle */
5314 icon_rect.left = ptItem.x + ptOrigin.x - 8;
5315 icon_rect.top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING;
5316 icon_rect.right = icon_rect.left + infoPtr->iconSize.cx + 16;
5317 icon_rect.bottom = icon_rect.top + infoPtr->iconSize.cy +
5318 ICON_TOP_PADDING;
5320 /* make label rectangle */
5321 /* Correct ptItem to icon upper-left */
5322 ptItem.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5323 ptItem.y -= ICON_TOP_PADDING;
5325 label_rect.left = ptItem.x + ptOrigin.x;
5326 label_rect.top = ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5328 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5329 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5331 label_rect.left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5332 label_rect.right = label_rect.left + nLabelWidth;
5333 label_rect.bottom = label_rect.top + infoPtr->ntmHeight + 1;
5334 InflateRect(&label_rect, 2, 0);
5336 else
5338 label_rect.right = label_rect.left + infoPtr->iconSpacing.cx - 1;
5339 label_rect.bottom = label_rect.top + infoPtr->nItemHeight;
5340 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, &label_rect);
5342 label_rect.bottom += HEIGHT_PADDING;
5343 bResult = TRUE;
5344 UnionRect (lprc, &icon_rect, &label_rect);
5348 else if (uView == LVS_SMALLICON)
5350 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5352 bResult = TRUE;
5353 lprc->left = ptItem.x + ptOrigin.x;
5354 lprc->right = lprc->left;
5355 lprc->top = ptItem.y + ptOrigin.y;
5356 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5357 if (infoPtr->himlState != NULL)
5358 lprc->right += infoPtr->iconSize.cx;
5359 if (infoPtr->himlSmall != NULL)
5360 lprc->right += infoPtr->iconSize.cx;
5362 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5363 nLabelWidth += TRAILING_PADDING;
5364 if (infoPtr->himlSmall)
5365 nLabelWidth += IMAGE_PADDING;
5366 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5367 lprc->right += nLabelWidth;
5368 else
5369 lprc->right = lprc->left + infoPtr->nItemWidth;
5372 else
5374 bResult = TRUE;
5375 lprc->left = ptItem.x;
5376 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5377 lprc->left += nIndent;
5378 lprc->right = lprc->left;
5379 lprc->top = ptItem.y;
5380 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5382 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5384 RECT br;
5385 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5386 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5388 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5390 else
5392 if (infoPtr->himlState != NULL)
5393 lprc->right += infoPtr->iconSize.cx;
5395 if (infoPtr->himlSmall != NULL)
5396 lprc->right += infoPtr->iconSize.cx;
5398 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5399 nLabelWidth += TRAILING_PADDING;
5400 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5401 lprc->right += nLabelWidth;
5402 else
5403 lprc->right = lprc->left + infoPtr->nItemWidth;
5406 break;
5408 case LVIR_SELECTBOUNDS:
5409 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptItem)) break;
5410 if (uView == LVS_ICON)
5412 if (infoPtr->himlNormal != NULL)
5414 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5416 bResult = TRUE;
5417 lprc->left = ptItem.x + ptOrigin.x;
5418 lprc->top = ptItem.y + ptOrigin.y;
5419 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5420 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5424 else if (uView == LVS_SMALLICON)
5426 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5428 bResult = TRUE;
5429 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5430 lprc->top = ptItem.y + ptOrigin.y;
5431 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5433 if (infoPtr->himlState != NULL)
5434 lprc->left += infoPtr->iconSize.cx;
5436 lprc->right = lprc->left;
5438 if (infoPtr->himlSmall != NULL)
5439 lprc->right += infoPtr->iconSize.cx;
5441 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5442 nLabelWidth += TRAILING_PADDING;
5443 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5444 lprc->right += nLabelWidth;
5445 else
5446 lprc->right = nLeftPos + infoPtr->nItemWidth;
5449 else
5451 bResult = TRUE;
5452 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5453 nLeftPos = lprc->left = ptItem.x + nIndent;
5454 else
5455 nLeftPos = lprc->left = ptItem.x;
5456 lprc->top = ptItem.y;
5457 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5459 if (infoPtr->himlState != NULL)
5460 lprc->left += infoPtr->iconSize.cx;
5462 lprc->right = lprc->left;
5464 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5466 RECT br;
5467 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5468 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5470 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5472 else
5474 if (infoPtr->himlSmall != NULL)
5475 lprc->right += infoPtr->iconSize.cx;
5477 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5478 nLabelWidth += TRAILING_PADDING;
5479 if (infoPtr->himlSmall)
5480 nLabelWidth += IMAGE_PADDING;
5481 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5482 lprc->right += nLabelWidth;
5483 else
5484 lprc->right = nLeftPos + infoPtr->nItemWidth;
5487 break;
5491 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5492 lprc->left, lprc->top, lprc->right, lprc->bottom);
5494 return bResult;
5498 static LRESULT LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem, INT
5499 flags, LPRECT lprc)
5501 UINT uView = LISTVIEW_GetType(infoPtr);
5502 INT count;
5504 TRACE("(nItem=%d, nSubItem=%d lprc=%p)\n", nItem, nSubItem,
5505 lprc);
5507 if (!(uView & LVS_REPORT))
5508 return FALSE;
5510 if (flags & LVIR_ICON)
5512 FIXME("Unimplemented LVIR_ICON\n");
5513 return FALSE;
5515 else
5517 int top = min(infoPtr->nColumnCount, nSubItem - 1);
5519 LISTVIEW_GetItemRect(infoPtr,nItem,lprc);
5520 for (count = 0; count < top; count++)
5521 lprc->left += LISTVIEW_GetColumnWidth(infoPtr,count);
5523 lprc->right = LISTVIEW_GetColumnWidth(infoPtr,(nSubItem-1)) +
5524 lprc->left;
5526 return TRUE;
5530 /***
5531 * DESCRIPTION:
5532 * Retrieves the width of a label.
5534 * PARAMETER(S):
5535 * [I] infoPtr : valid pointer to the listview structure
5537 * RETURN:
5538 * SUCCESS : string width (in pixels)
5539 * FAILURE : zero
5541 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5543 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5544 INT nLabelWidth = 0;
5545 LVITEMW lvItem;
5547 TRACE("(nItem=%d)\n", nItem);
5549 ZeroMemory(&lvItem, sizeof(lvItem));
5550 lvItem.mask = LVIF_TEXT;
5551 lvItem.iItem = nItem;
5552 lvItem.cchTextMax = DISP_TEXT_SIZE;
5553 lvItem.pszText = szDispText;
5554 if (LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE))
5555 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5557 return nLabelWidth;
5560 /***
5561 * DESCRIPTION:
5562 * Retrieves the spacing between listview control items.
5564 * PARAMETER(S):
5565 * [I] infoPtr : valid pointer to the listview structure
5566 * [I] BOOL : flag for small or large icon
5568 * RETURN:
5569 * Horizontal + vertical spacing
5571 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5573 LONG lResult;
5575 if (!bSmall)
5577 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5579 else
5581 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
5582 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5583 else
5584 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5586 return lResult;
5589 /***
5590 * DESCRIPTION:
5591 * Retrieves the state of a listview control item.
5593 * PARAMETER(S):
5594 * [I] infoPtr : valid pointer to the listview structure
5595 * [I] INT : item index
5596 * [I] UINT : state mask
5598 * RETURN:
5599 * State specified by the mask.
5601 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5603 LVITEMW lvItem;
5604 UINT uState = 0;
5606 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5608 ZeroMemory(&lvItem, sizeof(lvItem));
5609 lvItem.iItem = nItem;
5610 lvItem.stateMask = uMask;
5611 lvItem.mask = LVIF_STATE;
5612 if (LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE))
5613 uState = lvItem.state & uMask;
5616 return uState;
5619 /***
5620 * DESCRIPTION:
5621 * Retrieves the text of a listview control item or subitem.
5623 * PARAMETER(S):
5624 * [I] hwnd : window handle
5625 * [I] nItem : item index
5626 * [IO] lpLVItem : item information
5627 * [I] isW : TRUE if lpLVItem is Unicode
5629 * RETURN:
5630 * SUCCESS : string length
5631 * FAILURE : 0
5633 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5635 INT nLength = 0;
5637 if (lpLVItem != NULL)
5639 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5641 lpLVItem->mask = LVIF_TEXT;
5642 lpLVItem->iItem = nItem;
5643 if (LISTVIEW_GetItemT(infoPtr, lpLVItem, FALSE, isW))
5644 nLength = textlenT(lpLVItem->pszText, isW);
5648 return nLength;
5651 /***
5652 * DESCRIPTION:
5653 * Searches for an item based on properties + relationships.
5655 * PARAMETER(S):
5656 * [I] infoPtr : valid pointer to the listview structure
5657 * [I] INT : item index
5658 * [I] INT : relationship flag
5660 * RETURN:
5661 * SUCCESS : item index
5662 * FAILURE : -1
5664 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5666 UINT uView = LISTVIEW_GetType(infoPtr);
5667 UINT uMask = 0;
5668 LVFINDINFOW lvFindInfo;
5669 INT nCountPerColumn;
5670 INT i;
5672 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
5674 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5676 if (uFlags & LVNI_CUT)
5677 uMask |= LVIS_CUT;
5679 if (uFlags & LVNI_DROPHILITED)
5680 uMask |= LVIS_DROPHILITED;
5682 if (uFlags & LVNI_FOCUSED)
5683 uMask |= LVIS_FOCUSED;
5685 if (uFlags & LVNI_SELECTED)
5686 uMask |= LVIS_SELECTED;
5688 if (uFlags & LVNI_ABOVE)
5690 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5692 while (nItem >= 0)
5694 nItem--;
5695 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5696 return nItem;
5699 else
5701 lvFindInfo.flags = LVFI_NEARESTXY;
5702 lvFindInfo.vkDirection = VK_UP;
5703 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5704 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5706 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5707 return nItem;
5711 else if (uFlags & LVNI_BELOW)
5713 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5715 while (nItem < GETITEMCOUNT(infoPtr))
5717 nItem++;
5718 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5719 return nItem;
5722 else
5724 lvFindInfo.flags = LVFI_NEARESTXY;
5725 lvFindInfo.vkDirection = VK_DOWN;
5726 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5727 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5729 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5730 return nItem;
5734 else if (uFlags & LVNI_TOLEFT)
5736 if (uView == LVS_LIST)
5738 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5739 while (nItem - nCountPerColumn >= 0)
5741 nItem -= nCountPerColumn;
5742 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5743 return nItem;
5746 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5748 lvFindInfo.flags = LVFI_NEARESTXY;
5749 lvFindInfo.vkDirection = VK_LEFT;
5750 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5751 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5753 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5754 return nItem;
5758 else if (uFlags & LVNI_TORIGHT)
5760 if (uView == LVS_LIST)
5762 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5763 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5765 nItem += nCountPerColumn;
5766 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5767 return nItem;
5770 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5772 lvFindInfo.flags = LVFI_NEARESTXY;
5773 lvFindInfo.vkDirection = VK_RIGHT;
5774 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5775 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5777 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5778 return nItem;
5782 else
5784 nItem++;
5786 /* search by index */
5787 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5789 if ((ListView_GetItemState(infoPtr->hwndSelf, i, uMask) & uMask) == uMask)
5790 return i;
5795 return -1;
5798 /* LISTVIEW_GetNumberOfWorkAreas */
5800 /***
5801 * DESCRIPTION:
5802 * Retrieves the origin coordinates when in icon or small icon display mode.
5804 * PARAMETER(S):
5805 * [I] infoPtr : valid pointer to the listview structure
5806 * [O] LPPOINT : coordinate information
5808 * RETURN:
5809 * SUCCESS : TRUE
5810 * FAILURE : FALSE
5812 static LRESULT LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5814 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
5815 UINT uView = lStyle & LVS_TYPEMASK;
5816 BOOL bResult = FALSE;
5818 TRACE("(lpptOrigin=%p)\n", lpptOrigin);
5820 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5822 SCROLLINFO scrollInfo;
5823 ZeroMemory(lpptOrigin, sizeof(POINT));
5824 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5825 scrollInfo.cbSize = sizeof(SCROLLINFO);
5827 if (lStyle & WS_HSCROLL)
5829 scrollInfo.fMask = SIF_POS;
5830 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5831 lpptOrigin->x = -scrollInfo.nPos;
5834 if (lStyle & WS_VSCROLL)
5836 scrollInfo.fMask = SIF_POS;
5837 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5838 lpptOrigin->y = -scrollInfo.nPos;
5841 bResult = TRUE;
5843 TRACE("(pt=(%ld,%ld))\n", lpptOrigin->x, lpptOrigin->y);
5847 return bResult;
5850 /***
5851 * DESCRIPTION:
5852 * Retrieves the number of items that are marked as selected.
5854 * PARAMETER(S):
5855 * [I] infoPtr : valid pointer to the listview structure
5857 * RETURN:
5858 * Number of items selected.
5860 static LRESULT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
5862 /* REDO THIS */
5863 INT nSelectedCount = 0;
5864 INT i;
5866 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
5868 if (ListView_GetItemState(infoPtr->hwndSelf, i, LVIS_SELECTED) & LVIS_SELECTED)
5869 nSelectedCount++;
5872 return nSelectedCount;
5875 /***
5876 * DESCRIPTION:
5877 * Retrieves the width of a string.
5879 * PARAMETER(S):
5880 * [I] hwnd : window handle
5881 * [I] lpszText : text string to process
5882 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5884 * RETURN:
5885 * SUCCESS : string width (in pixels)
5886 * FAILURE : zero
5888 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5890 if (is_textT(lpszText, isW))
5892 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5893 HDC hdc = GetDC(infoPtr->hwndSelf);
5894 HFONT hOldFont = SelectObject(hdc, hFont);
5895 SIZE stringSize;
5896 ZeroMemory(&stringSize, sizeof(SIZE));
5897 if (isW)
5898 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5899 else
5900 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5901 SelectObject(hdc, hOldFont);
5902 ReleaseDC(infoPtr->hwndSelf, hdc);
5903 return stringSize.cx;
5905 return 0;
5908 /***
5909 * DESCRIPTION:
5910 * Retrieves the text backgound color.
5912 * PARAMETER(S):
5913 * [I] infoPtr : valid pointer to the listview structure
5915 * RETURN:
5916 * COLORREF associated with the the background.
5918 static LRESULT LISTVIEW_GetTextBkColor(LISTVIEW_INFO *infoPtr)
5920 return infoPtr->clrTextBk;
5923 /***
5924 * DESCRIPTION:
5925 * Retrieves the text color.
5927 * PARAMETER(S):
5928 * [I] infoPtr : valid pointer to the listview structure
5930 * RETURN:
5931 * COLORREF associated with the text.
5933 static LRESULT LISTVIEW_GetTextColor(LISTVIEW_INFO *infoPtr)
5935 return infoPtr->clrText;
5938 /***
5939 * DESCRIPTION:
5940 * Determines item if a hit or closest if not
5942 * PARAMETER(S):
5943 * [I] infoPtr : valid pointer to the listview structure
5944 * [IO] LPLV_INTHIT : hit test information
5945 * [I] subitem : fill out iSubItem.
5947 * RETURN:
5948 * SUCCESS : item index of hit
5949 * FAILURE : -1
5951 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *infoPtr, LPLV_INTHIT lpInt, BOOL subitem)
5953 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
5954 UINT uView = lStyle & LVS_TYPEMASK;
5955 INT i,j,topindex,bottomindex;
5956 RECT rcItem,rcSubItem;
5957 DWORD xterm, yterm, dist;
5959 TRACE("(x=%ld, y=%ld)\n", lpInt->ht.pt.x, lpInt->ht.pt.y);
5961 topindex = LISTVIEW_GetTopIndex(infoPtr);
5962 if (uView == LVS_REPORT)
5964 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
5965 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
5967 else
5969 bottomindex = GETITEMCOUNT(infoPtr);
5972 lpInt->distance = 0x7fffffff;
5973 lpInt->iDistItem = -1;
5975 for (i = topindex; i < bottomindex; i++)
5977 rcItem.left = LVIR_BOUNDS;
5978 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5980 if (PtInRect(&rcItem, lpInt->ht.pt))
5982 rcSubItem = rcItem;
5983 rcItem.left = LVIR_ICON;
5984 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5986 if (PtInRect(&rcItem, lpInt->ht.pt))
5988 lpInt->ht.flags = LVHT_ONITEMICON;
5989 lpInt->ht.iItem = i;
5990 goto set_subitem;
5994 rcItem.left = LVIR_LABEL;
5995 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5997 if (PtInRect(&rcItem, lpInt->ht.pt))
5999 lpInt->ht.flags = LVHT_ONITEMLABEL;
6000 lpInt->ht.iItem = i;
6001 goto set_subitem;
6005 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6006 lpInt->ht.iItem = i;
6007 set_subitem:
6008 if (subitem)
6010 lpInt->ht.iSubItem = 0;
6011 rcSubItem.right = rcSubItem.left;
6012 for (j = 0; j < infoPtr->nColumnCount; j++)
6014 rcSubItem.left = rcSubItem.right;
6015 rcSubItem.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6016 if (PtInRect(&rcSubItem, lpInt->ht.pt))
6018 lpInt->ht.iSubItem = j;
6019 break;
6023 return i;
6025 else
6028 * Now compute distance from point to center of boundary
6029 * box. Since we are only interested in the relative
6030 * distance, we can skip the nasty square root operation
6032 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6033 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6034 dist = xterm * xterm + yterm * yterm;
6035 if (dist < lpInt->distance)
6037 lpInt->distance = dist;
6038 lpInt->iDistItem = i;
6044 lpInt->ht.flags = LVHT_NOWHERE;
6045 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6047 return -1;
6050 /***
6051 * DESCRIPTION:
6052 * Determines which section of the item was selected (if any).
6054 * PARAMETER(S):
6055 * [I] infoPtr : valid pointer to the listview structure
6056 * [IO] LPLVHITTESTINFO : hit test information
6057 * [I] subitem : fill out iSubItem.
6059 * RETURN:
6060 * SUCCESS : item index
6061 * FAILURE : -1
6063 static INT LISTVIEW_HitTestItem(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6065 INT ret;
6066 LV_INTHIT lv_inthit;
6068 TRACE("(x=%ld, y=%ld)\n", lpHitTestInfo->pt.x,
6069 lpHitTestInfo->pt.y);
6071 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6072 ret = LISTVIEW_SuperHitTestItem(infoPtr, &lv_inthit, subitem);
6073 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6074 return ret;
6077 /***
6078 * DESCRIPTION:
6079 * Determines which listview item is located at the specified position.
6081 * PARAMETER(S):
6082 * [I] infoPtr : valid pointer to the listview structure
6083 * [IO} LPLVHITTESTINFO : hit test information
6085 * RETURN:
6086 * SUCCESS : item index
6087 * FAILURE : -1
6089 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpHitTestInfo)
6091 INT nItem = -1;
6093 lpHitTestInfo->flags = 0;
6095 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6096 lpHitTestInfo->flags = LVHT_TOLEFT;
6097 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6098 lpHitTestInfo->flags = LVHT_TORIGHT;
6099 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6100 lpHitTestInfo->flags |= LVHT_ABOVE;
6101 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6102 lpHitTestInfo->flags |= LVHT_BELOW;
6104 if (lpHitTestInfo->flags == 0)
6106 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6107 * an app might pass only a structure with space up to iItem!
6108 * (MS Office 97 does that for instance in the file open dialog)
6110 nItem = LISTVIEW_HitTestItem(infoPtr, lpHitTestInfo, FALSE);
6113 return nItem;
6116 /***
6117 * DESCRIPTION:
6118 * Determines which listview subitem is located at the specified position.
6120 * PARAMETER(S):
6121 * [I] infoPtr : valid pointer to the listview structure
6122 * [IO} LPLVHITTESTINFO : hit test information
6124 * RETURN:
6125 * SUCCESS : item index
6126 * FAILURE : -1
6128 static LRESULT LISTVIEW_SubItemHitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpHitTestInfo)
6130 INT nItem = -1;
6132 lpHitTestInfo->flags = 0;
6134 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6135 lpHitTestInfo->flags = LVHT_TOLEFT;
6136 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6137 lpHitTestInfo->flags = LVHT_TORIGHT;
6138 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6139 lpHitTestInfo->flags |= LVHT_ABOVE;
6140 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6141 lpHitTestInfo->flags |= LVHT_BELOW;
6143 if (lpHitTestInfo->flags == 0)
6144 nItem = LISTVIEW_HitTestItem(infoPtr, lpHitTestInfo, TRUE);
6146 return nItem;
6149 /***
6150 * DESCRIPTION:
6151 * Inserts a new column.
6153 * PARAMETER(S):
6154 * [I] infoPtr : valid pointer to the listview structure
6155 * [I] INT : column index
6156 * [I] LPLVCOLUMNW : column information
6158 * RETURN:
6159 * SUCCESS : new column index
6160 * FAILURE : -1
6162 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6163 LPLVCOLUMNW lpColumn, BOOL isW)
6165 INT nNewColumn = -1;
6166 HDITEMW hdi;
6168 TRACE("(nColumn=%d, lpColumn=%p)\n", nColumn, lpColumn);
6170 if (lpColumn != NULL)
6172 /* initialize memory */
6173 ZeroMemory(&hdi, sizeof(hdi));
6175 if (lpColumn->mask & LVCF_FMT)
6177 /* format member is valid */
6178 hdi.mask |= HDI_FORMAT;
6180 /* set text alignment (leftmost column must be left-aligned) */
6181 if (nColumn == 0)
6183 hdi.fmt |= HDF_LEFT;
6185 else
6187 if (lpColumn->fmt & LVCFMT_LEFT)
6189 hdi.fmt |= HDF_LEFT;
6191 else if (lpColumn->fmt & LVCFMT_RIGHT)
6193 hdi.fmt |= HDF_RIGHT;
6195 else if (lpColumn->fmt & LVCFMT_CENTER)
6197 hdi.fmt |= HDF_CENTER;
6201 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6203 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6204 /* ??? */
6207 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6209 /* ??? */
6212 if (lpColumn->fmt & LVCFMT_IMAGE)
6214 hdi.fmt |= HDF_IMAGE;
6215 hdi.iImage = I_IMAGECALLBACK;
6219 if (lpColumn->mask & LVCF_WIDTH)
6221 hdi.mask |= HDI_WIDTH;
6222 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6224 /* make it fill the remainder of the controls width */
6225 HDITEMW hdit;
6226 RECT rcHeader;
6227 INT item_index;
6229 ZeroMemory(&hdit, sizeof(hdit));
6231 /* get the width of every item except the current one */
6232 hdit.mask = HDI_WIDTH;
6233 hdi.cxy = 0;
6235 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6236 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6237 hdi.cxy+=hdit.cxy;
6240 /* retrieve the layout of the header */
6241 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6242 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6243 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6245 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6247 else
6248 hdi.cxy = lpColumn->cx;
6251 if (lpColumn->mask & LVCF_TEXT)
6253 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6254 hdi.pszText = lpColumn->pszText;
6255 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6256 hdi.fmt |= HDF_STRING;
6259 if (lpColumn->mask & LVCF_IMAGE)
6261 hdi.mask |= HDI_IMAGE;
6262 hdi.iImage = lpColumn->iImage;
6265 if (lpColumn->mask & LVCF_ORDER)
6267 hdi.mask |= HDI_ORDER;
6268 hdi.iOrder = lpColumn->iOrder;
6271 /* insert item in header control */
6272 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6273 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6274 (WPARAM)nColumn, (LPARAM)&hdi);
6276 /* Need to reset the item width when inserting a new column */
6277 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
6279 LISTVIEW_UpdateScroll(infoPtr);
6280 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
6283 return nNewColumn;
6286 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6287 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6288 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6289 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6290 their own sort proc. when sending LVM_SORTITEMS.
6292 /* Platform SDK:
6293 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6295 LVS_SORTXXX must be specified,
6296 LVS_OWNERDRAW is not set,
6297 <item>.pszText is not LPSTR_TEXTCALLBACK.
6299 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6300 are sorted based on item text..."
6302 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6304 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6305 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6306 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6307 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6309 /* if we're sorting descending, negate the return value */
6310 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6313 /***
6314 * nESCRIPTION:
6315 * Inserts a new item in the listview control.
6317 * PARAMETER(S):
6318 * [I] infoPtr : valid pointer to the listview structure
6319 * [I] LPLVITEMW : item information
6320 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6322 * RETURN:
6323 * SUCCESS : new item index
6324 * FAILURE : -1
6326 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6328 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
6329 UINT uView = lStyle & LVS_TYPEMASK;
6330 INT nItem = -1;
6331 HDPA hdpaSubItems;
6332 NMLISTVIEW nmlv;
6333 LISTVIEW_ITEM *lpItem;
6334 BOOL is_sorted;
6336 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6338 if (lStyle & LVS_OWNERDATA)
6340 nItem = infoPtr->hdpaItems->nItemCount;
6341 infoPtr->hdpaItems->nItemCount++;
6342 return nItem;
6345 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6346 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6348 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
6350 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6351 return -1;
6353 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6355 /* insert item in listview control data structure */
6356 if ( (hdpaSubItems = DPA_Create(8)) )
6357 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
6358 if (nItem == -1) goto fail;
6360 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6361 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6363 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6364 is_sorted ? GETITEMCOUNT( infoPtr ) + 1 : lpLVItem->iItem,
6365 hdpaSubItems );
6366 if (nItem == -1) goto fail;
6368 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW)) goto fail;
6370 /* if we're sorted, sort the list, and update the index */
6371 if (is_sorted)
6373 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr->hwndSelf );
6374 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6375 if (nItem == -1) goto fail;
6378 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6380 lpItem->valid = TRUE;
6382 /* send LVN_INSERTITEM notification */
6383 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6384 nmlv.iItem = nItem;
6385 nmlv.lParam = lpItem->lParam;
6386 listview_notify(infoPtr, LVN_INSERTITEM, &nmlv);
6388 /* align items (set position of each item) */
6389 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6391 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
6392 else LISTVIEW_AlignTop(infoPtr);
6395 LISTVIEW_UpdateScroll(infoPtr);
6397 /* FIXME: refresh client area */
6398 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
6400 return nItem;
6402 fail:
6403 DPA_Destroy (hdpaSubItems);
6404 COMCTL32_Free (lpItem);
6405 return -1;
6408 /***
6409 * DESCRIPTION:
6410 * Redraws a range of items.
6412 * PARAMETER(S):
6413 * [I] infoPtr : valid pointer to the listview structure
6414 * [I] INT : first item
6415 * [I] INT : last item
6417 * RETURN:
6418 * SUCCESS : TRUE
6419 * FAILURE : FALSE
6421 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6423 RECT rcItem;
6424 INT i;
6426 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6427 max(nFirst, nLast) >= GETITEMCOUNT(infoPtr))
6428 return FALSE;
6430 for (i = nFirst; i <= nLast; i++)
6432 rcItem.left = LVIR_BOUNDS;
6433 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
6434 InvalidateRect(infoPtr->hwndSelf, &rcItem, TRUE);
6437 return TRUE;
6440 /***
6441 * DESCRIPTION:
6442 * Scroll the content of a listview.
6444 * PARAMETER(S):
6445 * [I] infoPtr : valid pointer to the listview structure
6446 * [I] INT : horizontal scroll amount in pixels
6447 * [I] INT : vertical scroll amount in pixels
6449 * RETURN:
6450 * SUCCESS : TRUE
6451 * FAILURE : FALSE
6453 * COMMENTS:
6454 * If the control is in report mode (LVS_REPORT) the control can
6455 * be scrolled only in line increments. "dy" will be rounded to the
6456 * nearest number of pixels that are a whole line. Ex: if line height
6457 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6458 * is passed the the scroll will be 0. (per MSDN 7/2002)
6460 * For: (per experimentaion with native control and CSpy ListView)
6461 * LVS_ICON dy=1 = 1 pixel (vertical only)
6462 * dx ignored
6463 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6464 * dx ignored
6465 * LVS_LIST dx=1 = 1 column (horizontal only)
6466 * but will only scroll 1 column per message
6467 * no matter what the value.
6468 * dy must be 0 or FALSE returned.
6469 * LVS_REPORT dx=1 = 1 pixel
6470 * dy= see above
6473 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6475 switch(LISTVIEW_GetType(infoPtr)) {
6476 case LVS_REPORT:
6477 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6478 dy /= infoPtr->nItemHeight;
6479 break;
6480 case LVS_LIST:
6481 if (dy != 0) return FALSE;
6482 break;
6483 default: /* icon */
6484 dx = 0;
6485 break;
6488 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6489 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6491 return TRUE;
6494 /***
6495 * DESCRIPTION:
6496 * Sets the background color.
6498 * PARAMETER(S):
6499 * [I] infoPtr : valid pointer to the listview structure
6500 * [I] COLORREF : background color
6502 * RETURN:
6503 * SUCCESS : TRUE
6504 * FAILURE : FALSE
6506 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6508 if(infoPtr->clrBk != clrBk) {
6509 infoPtr->clrBk = clrBk;
6510 if (infoPtr->hBkBrush) DeleteObject(infoPtr->hBkBrush);
6511 infoPtr->hBkBrush = clrBk == CLR_NONE ? 0 : CreateSolidBrush(clrBk);
6512 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
6515 return TRUE;
6518 /* LISTVIEW_SetBkImage */
6520 /***
6521 * DESCRIPTION:
6522 * Sets the attributes of a header item.
6524 * PARAMETER(S):
6525 * [I] infoPtr : valid pointer to the listview structure
6526 * [I] INT : column index
6527 * [I] LPLVCOLUMNW : column attributes
6528 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6529 * otherwise it is in fact a LPLVCOLUMNA
6531 * RETURN:
6532 * SUCCESS : TRUE
6533 * FAILURE : FALSE
6535 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6536 LPLVCOLUMNW lpColumn, BOOL isW)
6538 BOOL bResult = FALSE;
6539 HDITEMW hdi, hdiget;
6541 if ((lpColumn != NULL) && (nColumn >= 0) &&
6542 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6544 /* initialize memory */
6545 ZeroMemory(&hdi, sizeof(hdi));
6547 if (lpColumn->mask & LVCF_FMT)
6549 /* format member is valid */
6550 hdi.mask |= HDI_FORMAT;
6552 /* get current format first */
6553 hdiget.mask = HDI_FORMAT;
6554 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6555 /* preserve HDF_STRING if present */
6556 hdi.fmt = hdiget.fmt & HDF_STRING;
6558 /* set text alignment (leftmost column must be left-aligned) */
6559 if (nColumn == 0)
6561 hdi.fmt |= HDF_LEFT;
6563 else
6565 if (lpColumn->fmt & LVCFMT_LEFT)
6566 hdi.fmt |= HDF_LEFT;
6567 else if (lpColumn->fmt & LVCFMT_RIGHT)
6568 hdi.fmt |= HDF_RIGHT;
6569 else if (lpColumn->fmt & LVCFMT_CENTER)
6570 hdi.fmt |= HDF_CENTER;
6573 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6574 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6576 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6577 hdi.fmt |= HDF_IMAGE;
6579 if (lpColumn->fmt & LVCFMT_IMAGE)
6581 hdi.fmt |= HDF_IMAGE;
6582 hdi.iImage = I_IMAGECALLBACK;
6586 if (lpColumn->mask & LVCF_WIDTH)
6588 hdi.mask |= HDI_WIDTH;
6589 hdi.cxy = lpColumn->cx;
6592 if (lpColumn->mask & LVCF_TEXT)
6594 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6595 hdi.pszText = lpColumn->pszText;
6596 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6597 hdi.fmt |= HDF_STRING;
6600 if (lpColumn->mask & LVCF_IMAGE)
6602 hdi.mask |= HDI_IMAGE;
6603 hdi.iImage = lpColumn->iImage;
6606 if (lpColumn->mask & LVCF_ORDER)
6608 hdi.mask |= HDI_ORDER;
6609 hdi.iOrder = lpColumn->iOrder;
6612 /* set header item attributes */
6613 if (isW)
6614 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6615 else
6616 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6619 return bResult;
6622 /***
6623 * DESCRIPTION:
6624 * Sets the column order array
6626 * PARAMETERS:
6627 * [I] infoPtr : valid pointer to the listview structure
6628 * [I] INT : number of elements in column order array
6629 * [I] INT : pointer to column order array
6631 * RETURN:
6632 * SUCCESS : TRUE
6633 * FAILURE : FALSE
6635 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6637 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6639 if (!lpiArray)
6640 return FALSE;
6642 return TRUE;
6646 /***
6647 * DESCRIPTION:
6648 * Sets the width of a column
6650 * PARAMETERS:
6651 * [I] infoPtr : valid pointer to the listview structure
6652 * [I] INT : column index
6653 * [I] INT : column width
6655 * RETURN:
6656 * SUCCESS : TRUE
6657 * FAILURE : FALSE
6659 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6661 HDITEMW hdi;
6662 LRESULT lret;
6663 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
6664 UINT uView = lStyle & LVS_TYPEMASK;
6665 HDC hdc;
6666 HFONT header_font;
6667 HFONT old_font;
6668 SIZE size;
6669 WCHAR text_buffer[DISP_TEXT_SIZE];
6670 INT header_item_count;
6671 INT item_index;
6672 INT nLabelWidth;
6673 RECT rcHeader;
6674 LVITEMW lvItem;
6675 WCHAR szDispText[DISP_TEXT_SIZE];
6677 if (!infoPtr->hwndHeader) /* make sure we have a header */
6678 return (FALSE);
6680 /* set column width only if in report or list mode */
6681 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6682 return (FALSE);
6684 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6686 /* take care of invalid cx values */
6687 if((uView == LVS_REPORT) && (cx < -2))
6688 cx = LVSCW_AUTOSIZE;
6689 else if (uView == LVS_LIST && (cx < 1))
6690 return FALSE;
6692 /* resize all columns if in LVS_LIST mode */
6693 if(uView == LVS_LIST) {
6694 infoPtr->nItemWidth = cx;
6695 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); /* force redraw of the listview */
6696 return TRUE;
6699 /* autosize based on listview items width */
6700 if(cx == LVSCW_AUTOSIZE)
6702 /* set the width of the column to the width of the widest item */
6703 if (iCol == 0 || uView == LVS_LIST)
6705 cx = 0;
6706 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6708 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6709 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6711 if (infoPtr->himlSmall)
6712 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6714 else
6716 ZeroMemory(&lvItem, sizeof(lvItem));
6717 lvItem.iSubItem = iCol;
6718 lvItem.mask = LVIF_TEXT;
6719 lvItem.cchTextMax = DISP_TEXT_SIZE;
6720 lvItem.pszText = szDispText;
6721 *lvItem.pszText = '\0';
6722 cx = 0;
6723 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6725 lvItem.iItem = item_index;
6726 LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, TRUE);
6727 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6728 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6731 cx += TRAILING_PADDING;
6732 } /* autosize based on listview header width */
6733 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6735 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6737 /* if iCol is the last column make it fill the remainder of the controls width */
6738 if(iCol == (header_item_count - 1)) {
6739 /* get the width of every item except the current one */
6740 hdi.mask = HDI_WIDTH;
6741 cx = 0;
6743 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6744 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6745 cx+=hdi.cxy;
6748 /* retrieve the layout of the header */
6749 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6751 cx = (rcHeader.right - rcHeader.left) - cx;
6753 else
6755 /* Despite what the MS docs say, if this is not the last
6756 column, then MS resizes the column to the width of the
6757 largest text string in the column, including headers
6758 and items. This is different from LVSCW_AUTOSIZE in that
6759 LVSCW_AUTOSIZE ignores the header string length.
6762 /* retrieve header font */
6763 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6765 /* retrieve header text */
6766 hdi.mask = HDI_TEXT;
6767 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6768 hdi.pszText = text_buffer;
6770 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6772 /* determine the width of the text in the header */
6773 hdc = GetDC(infoPtr->hwndSelf);
6774 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6776 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6778 SelectObject(hdc, old_font); /* restore the old font */
6779 ReleaseDC(infoPtr->hwndSelf, hdc);
6781 ZeroMemory(&lvItem, sizeof(lvItem));
6782 lvItem.iSubItem = iCol;
6783 lvItem.mask = LVIF_TEXT;
6784 lvItem.cchTextMax = DISP_TEXT_SIZE;
6785 lvItem.pszText = szDispText;
6786 *lvItem.pszText = '\0';
6787 cx = size.cx;
6788 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6790 lvItem.iItem = item_index;
6791 LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, TRUE);
6792 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6793 nLabelWidth += TRAILING_PADDING;
6794 /* While it is possible for subitems to have icons, even MS messes
6795 up the positioning, so I suspect no applications actually use
6796 them. */
6797 if (item_index == 0 && infoPtr->himlSmall)
6798 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6799 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6804 /* call header to update the column change */
6805 hdi.mask = HDI_WIDTH;
6807 hdi.cxy = cx;
6808 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6810 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); /* force redraw of the listview */
6812 return lret;
6815 /***
6816 * DESCRIPTION:
6817 * Sets the extended listview style.
6819 * PARAMETERS:
6820 * [I] infoPtr : valid pointer to the listview structure
6821 * [I] DWORD : mask
6822 * [I] DWORD : style
6824 * RETURN:
6825 * SUCCESS : previous style
6826 * FAILURE : 0
6828 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6830 DWORD dwOldStyle = infoPtr->dwExStyle;
6832 /* set new style */
6833 if (dwMask)
6834 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6835 else
6836 infoPtr->dwExStyle = dwStyle;
6838 return dwOldStyle;
6841 /***
6842 * DESCRIPTION:
6843 * Sets the new hot cursor used during hot tracking and hover selection.
6845 * PARAMETER(S):
6846 * [I] infoPtr : valid pointer to the listview structure
6847 * [I} hCurosr : the new hot cursor handle
6849 * RETURN:
6850 * Returns the previous hot cursor
6852 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6854 HCURSOR oldCursor = infoPtr->hHotCursor;
6855 infoPtr->hHotCursor = hCursor;
6856 return oldCursor;
6860 /***
6861 * DESCRIPTION:
6862 * Sets the hot item index.
6864 * PARAMETERS:
6865 * [I] infoPtr : valid pointer to the listview structure
6866 * [I] INT : index
6868 * RETURN:
6869 * SUCCESS : previous hot item index
6870 * FAILURE : -1 (no hot item)
6872 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6874 INT iOldIndex = infoPtr->nHotItem;
6875 infoPtr->nHotItem = iIndex;
6876 return iOldIndex;
6880 /***
6881 * DESCRIPTION:
6882 * Sets the amount of time the cursor must hover over an item before it is selected.
6884 * PARAMETER(S):
6885 * [I] infoPtr : valid pointer to the listview structure
6886 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6888 * RETURN:
6889 * Returns the previous hover time
6891 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6893 DWORD oldHoverTime = infoPtr->dwHoverTime;
6894 infoPtr->dwHoverTime = dwHoverTime;
6895 return oldHoverTime;
6898 /***
6899 * DESCRIPTION:
6900 * Sets spacing for icons of LVS_ICON style.
6902 * PARAMETER(S):
6903 * [I] infoPtr : valid pointer to the listview structure
6904 * [I] DWORD : MAKELONG(cx, cy)
6906 * RETURN:
6907 * MAKELONG(oldcx, oldcy)
6909 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6911 INT cy = HIWORD(spacing);
6912 INT cx = LOWORD(spacing);
6913 DWORD oldspacing;
6914 LONG lStyle = GetWindowLongA(infoPtr->hwndSelf, GWL_STYLE);
6915 UINT uView = lStyle & LVS_TYPEMASK;
6917 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6918 if (cx == -1) /* set to default */
6919 cx = GetSystemMetrics(SM_CXICONSPACING);
6920 if (cy == -1) /* set to default */
6921 cy = GetSystemMetrics(SM_CYICONSPACING);
6923 if (cx)
6924 infoPtr->iconSpacing.cx = cx;
6925 else
6926 { /* if 0 then compute width */
6927 if (uView == LVS_ICON)
6928 FIXME("width computation not yet done\n");
6930 * Should scan each item and determine max width of
6931 * icon or label, then make that the width
6933 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
6934 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(infoPtr);
6936 if (cy)
6937 infoPtr->iconSpacing.cy = cy;
6938 else
6939 { /* if 0 then compute height */
6940 if (uView == LVS_ICON)
6941 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight
6942 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6944 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
6945 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(infoPtr);
6948 TRACE("old=(%d,%d), new=(%ld,%ld), iconSize=(%ld,%ld), ntmH=%d\n",
6949 LOWORD(oldspacing), HIWORD(oldspacing),
6950 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy,
6951 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6952 infoPtr->ntmHeight);
6954 /* these depend on the iconSpacing */
6955 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
6956 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6958 return oldspacing;
6961 /***
6962 * DESCRIPTION:
6963 * Sets image lists.
6965 * PARAMETER(S):
6966 * [I] infoPtr : valid pointer to the listview structure
6967 * [I] INT : image list type
6968 * [I] HIMAGELIST : image list handle
6970 * RETURN:
6971 * SUCCESS : old image list
6972 * FAILURE : NULL
6974 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6976 HIMAGELIST himlOld = 0;
6977 INT oldHeight;
6978 UINT uView = LISTVIEW_GetType(infoPtr);
6980 switch (nType)
6982 case LVSIL_NORMAL:
6983 himlOld = infoPtr->himlNormal;
6984 infoPtr->himlNormal = himl;
6985 if(himl && (LVS_ICON == uView))
6987 INT cx, cy;
6988 ImageList_GetIconSize(himl, &cx, &cy);
6989 TRACE("icon old size=(%ld,%ld), new size=(%d,%d)\n",
6990 infoPtr->iconSize.cx, infoPtr->iconSize.cy, cx, cy);
6991 infoPtr->iconSize.cx = cx;
6992 infoPtr->iconSize.cy = cy;
6993 LISTVIEW_SetIconSpacing(infoPtr,0);
6995 break;
6997 case LVSIL_SMALL:
6998 himlOld = infoPtr->himlSmall;
6999 infoPtr->himlSmall = himl;
7000 break;
7002 case LVSIL_STATE:
7003 himlOld = infoPtr->himlState;
7004 infoPtr->himlState = himl;
7005 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7006 break;
7009 oldHeight = infoPtr->nItemHeight;
7010 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7011 if (infoPtr->nItemHeight != oldHeight)
7012 LISTVIEW_UpdateScroll(infoPtr);
7014 return himlOld;
7017 /***
7018 * DESCRIPTION:
7019 * Preallocates memory (does *not* set the actual count of items !)
7021 * PARAMETER(S):
7022 * [I] infoPtr : valid pointer to the listview structure
7023 * [I] INT : item count (projected number of items to allocate)
7024 * [I] DWORD : update flags
7026 * RETURN:
7027 * SUCCESS : TRUE
7028 * FAILURE : FALSE
7030 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7032 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7034 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_OWNERDATA)
7036 int precount,topvisible;
7038 TRACE("LVS_OWNERDATA is set!\n");
7039 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7040 FIXME("flags %s %s not implemented\n",
7041 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7042 : "",
7043 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7046 * Internally remove all the selections.
7050 LISTVIEW_SELECTION *selection;
7051 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7052 if (selection)
7053 LISTVIEW_RemoveSelectionRange(infoPtr,selection->lower,
7054 selection->upper);
7056 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7058 precount = infoPtr->hdpaItems->nItemCount;
7059 topvisible = ListView_GetTopIndex(infoPtr->hwndSelf) +
7060 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
7062 infoPtr->hdpaItems->nItemCount = nItems;
7064 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(infoPtr),
7065 DEFAULT_COLUMN_WIDTH);
7067 LISTVIEW_UpdateSize(infoPtr);
7068 LISTVIEW_UpdateScroll(infoPtr);
7070 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7071 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7073 else
7075 /* According to MSDN for non-LVS_OWNERDATA this is just
7076 * a performance issue. The control allocates its internal
7077 * data structures for the number of items specified. It
7078 * cuts down on the number of memory allocations. Therefore
7079 * we will just issue a WARN here
7081 WARN("for non-ownerdata performance option not implemented.\n");
7084 return TRUE;
7087 /***
7088 * DESCRIPTION:
7089 * Sets the position of an item.
7091 * PARAMETER(S):
7092 * [I] infoPtr : valid pointer to the listview structure
7093 * [I] INT : item index
7094 * [I] LONG : x coordinate
7095 * [I] LONG : y coordinate
7097 * RETURN:
7098 * SUCCESS : TRUE
7099 * FAILURE : FALSE
7101 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem,
7102 LONG nPosX, LONG nPosY)
7104 UINT lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7105 UINT uView = lStyle & LVS_TYPEMASK;
7106 LISTVIEW_ITEM *lpItem;
7107 HDPA hdpaSubItems;
7108 BOOL bResult = FALSE;
7110 TRACE("(nItem=%d, X=%ld, Y=%ld)\n", nItem, nPosX, nPosY);
7112 if (lStyle & LVS_OWNERDATA)
7113 return FALSE;
7115 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7117 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7119 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7121 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7123 POINT orig;
7124 bResult = TRUE;
7125 orig = lpItem->ptPosition;
7126 if ((nPosX == -1) && (nPosY == -1))
7128 /* This point value seems to be an undocumented feature. The
7129 * best guess is that it means either at the origin, or at
7130 * the true beginning of the list. I will assume the origin.
7132 POINT pt1;
7133 if (!LISTVIEW_GetOrigin(infoPtr, &pt1))
7135 pt1.x = 0;
7136 pt1.y = 0;
7138 nPosX = pt1.x;
7139 nPosY = pt1.y;
7140 if (uView == LVS_ICON)
7142 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7143 nPosY += ICON_TOP_PADDING;
7145 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7146 nPosX, nPosY);
7149 lpItem->ptPosition.x = nPosX;
7150 lpItem->ptPosition.y = nPosY;
7151 if (uView == LVS_ICON)
7153 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7154 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7155 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7157 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7158 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7161 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7162 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7165 else
7167 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7168 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7176 return bResult;
7179 /***
7180 * DESCRIPTION:
7181 * Sets the state of one or many items.
7183 * PARAMETER(S):
7184 * [I] infoPtr : valid pointer to the listview structure
7185 * [I]INT : item index
7186 * [I] LPLVITEM : item or subitem info
7188 * RETURN:
7189 * SUCCESS : TRUE
7190 * FAILURE : FALSE
7192 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
7194 BOOL bResult = TRUE;
7195 LVITEMW lvItem;
7197 TRACE("(nItem=%d, lpLVItem=%s)\n",
7198 nItem, debuglvitem_t(lpLVItem, TRUE));
7200 ZeroMemory(&lvItem, sizeof(lvItem));
7201 lvItem.mask = LVIF_STATE;
7202 lvItem.state = lpLVItem->state;
7203 lvItem.stateMask = lpLVItem->stateMask ;
7204 lvItem.iItem = nItem;
7206 if (nItem == -1)
7208 /* apply to all items */
7209 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7210 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7212 else
7213 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7215 return bResult;
7218 /***
7219 * DESCRIPTION:
7220 * Sets the text of an item or subitem.
7222 * PARAMETER(S):
7223 * [I] hwnd : window handle
7224 * [I] nItem : item index
7225 * [I] lpLVItem : item or subitem info
7226 * [I] isW : TRUE if input is Unicode
7228 * RETURN:
7229 * SUCCESS : TRUE
7230 * FAILURE : FALSE
7232 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7234 BOOL bResult = FALSE;
7235 LVITEMW lvItem;
7237 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n",
7238 nItem, debuglvitem_t(lpLVItem, isW), isW);
7240 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7242 ZeroMemory(&lvItem, sizeof(LVITEMW));
7243 lvItem.mask = LVIF_TEXT;
7244 lvItem.pszText = lpLVItem->pszText;
7245 lvItem.iItem = nItem;
7246 lvItem.iSubItem = lpLVItem->iSubItem;
7247 if(isW) bResult = ListView_SetItemW(infoPtr->hwndSelf, &lvItem);
7248 else bResult = ListView_SetItemA(infoPtr->hwndSelf, &lvItem);
7251 return bResult;
7254 /***
7255 * DESCRIPTION:
7256 * Set item index that marks the start of a multiple selection.
7258 * PARAMETER(S):
7259 * [I] infoPtr : valid pointer to the listview structure
7260 * [I] INT : index
7262 * RETURN:
7263 * Index number or -1 if there is no selection mark.
7265 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7267 INT nOldIndex = infoPtr->nSelectionMark;
7269 TRACE("(nIndex=%d)\n", nIndex);
7271 infoPtr->nSelectionMark = nIndex;
7273 return nOldIndex;
7276 /***
7277 * DESCRIPTION:
7278 * Sets the text background color.
7280 * PARAMETER(S):
7281 * [I] infoPtr : valid pointer to the listview structure
7282 * [I] COLORREF : text background color
7284 * RETURN:
7285 * SUCCESS : TRUE
7286 * FAILURE : FALSE
7288 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7290 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7292 if (infoPtr->clrTextBk != clrTextBk)
7294 infoPtr->clrTextBk = clrTextBk;
7295 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7298 return TRUE;
7301 /***
7302 * DESCRIPTION:
7303 * Sets the text foreground color.
7305 * PARAMETER(S):
7306 * [I] infoPtr : valid pointer to the listview structure
7307 * [I] COLORREF : text color
7309 * RETURN:
7310 * SUCCESS : TRUE
7311 * FAILURE : FALSE
7313 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7315 TRACE("(clrText=%lx)\n", clrText);
7317 if (infoPtr->clrText != clrText)
7319 infoPtr->clrText = clrText;
7320 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7323 return TRUE;
7326 /* LISTVIEW_SetToolTips */
7327 /* LISTVIEW_SetUnicodeFormat */
7328 /* LISTVIEW_SetWorkAreas */
7330 /***
7331 * DESCRIPTION:
7332 * Callback internally used by LISTVIEW_SortItems()
7334 * PARAMETER(S):
7335 * [I] LPVOID : first LISTVIEW_ITEM to compare
7336 * [I] LPVOID : second LISTVIEW_ITEM to compare
7337 * [I] LPARAM : HWND of control
7339 * RETURN:
7340 * if first comes before second : negative
7341 * if first comes after second : positive
7342 * if first and second are equivalent : zero
7344 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7346 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7347 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7348 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7350 /* Forward the call to the client defined callback */
7351 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7354 /***
7355 * DESCRIPTION:
7356 * Sorts the listview items.
7358 * PARAMETER(S):
7359 * [I] infoPtr : valid pointer to the listview structure
7360 * [I] WPARAM : application-defined value
7361 * [I] LPARAM : pointer to comparision callback
7363 * RETURN:
7364 * SUCCESS : TRUE
7365 * FAILURE : FALSE
7367 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7369 UINT lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7370 HDPA hdpaSubItems;
7371 LISTVIEW_ITEM *lpItem;
7372 LPVOID selectionMarkItem;
7373 int i;
7375 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7377 if (lStyle & LVS_OWNERDATA) return FALSE;
7379 if (!infoPtr->hdpaItems) return FALSE;
7381 /* if there are 0 or 1 items, there is no need to sort */
7382 if (GETITEMCOUNT(infoPtr) < 2) return TRUE;
7384 if (infoPtr->nFocusedItem >= 0)
7386 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7387 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7388 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7391 infoPtr->pfnCompare = pfnCompare;
7392 infoPtr->lParamSort = lParamSort;
7393 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
7395 /* Adjust selections and indices so that they are the way they should
7396 * be after the sort (otherwise, the list items move around, but
7397 * whatever is at the item's previous original position will be
7398 * selected instead)
7400 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7401 for (i=0; i < GETITEMCOUNT(infoPtr); i++)
7403 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7404 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7406 if (lpItem->state & LVIS_SELECTED)
7407 LISTVIEW_AddSelectionRange(infoPtr, i, i);
7408 else
7409 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
7410 if (lpItem->state & LVIS_FOCUSED)
7412 infoPtr->nFocusedItem = i;
7413 lpItem->state &= ~LVIS_FOCUSED;
7416 if (selectionMarkItem != NULL)
7417 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7418 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7420 /* align the items */
7421 LISTVIEW_AlignTop(infoPtr);
7423 /* refresh the display */
7424 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7426 return TRUE;
7429 /* LISTVIEW_SubItemHitTest */
7431 /***
7432 * DESCRIPTION:
7433 * Updates an items or rearranges the listview control.
7435 * PARAMETER(S):
7436 * [I] infoPtr : valid pointer to the listview structure
7437 * [I] INT : item index
7439 * RETURN:
7440 * SUCCESS : TRUE
7441 * FAILURE : FALSE
7443 static LRESULT LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7445 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7446 BOOL bResult = FALSE;
7447 RECT rc;
7449 TRACE("(nItem=%d)\n", nItem);
7451 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7453 bResult = TRUE;
7455 /* rearrange with default alignment style */
7456 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7457 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7459 ListView_Arrange(infoPtr->hwndSelf, 0);
7461 else
7463 /* get item bounding rectangle */
7464 ListView_GetItemRect(infoPtr->hwndSelf, nItem, &rc, LVIR_BOUNDS);
7465 InvalidateRect(infoPtr->hwndSelf, &rc, TRUE);
7469 return bResult;
7472 /***
7473 * DESCRIPTION:
7474 * Creates the listview control.
7476 * PARAMETER(S):
7477 * [I] hwnd : window handle
7478 * [I] lpcs : the create parameters
7480 * RETURN:
7481 * Success: 0
7482 * Failure: -1
7484 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7486 LISTVIEW_INFO *infoPtr;
7487 UINT uView = lpcs->style & LVS_TYPEMASK;
7488 LOGFONTW logFont;
7490 TRACE("(lpcs=%p)\n", lpcs);
7492 /* initialize info pointer */
7493 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7494 if (!infoPtr) return -1;
7496 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7497 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7499 infoPtr->hwndSelf = hwnd;
7500 /* determine the type of structures to use */
7501 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7502 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7504 /* initialize color information */
7505 infoPtr->clrBk = comctl32_color.clrWindow;
7506 infoPtr->clrText = comctl32_color.clrWindowText;
7507 infoPtr->clrTextBk = CLR_DEFAULT;
7508 infoPtr->hBkBrush = CreateSolidBrush(infoPtr->clrBk);
7510 /* set default values */
7511 infoPtr->uCallbackMask = 0;
7512 infoPtr->nFocusedItem = -1;
7513 infoPtr->nSelectionMark = -1;
7514 infoPtr->nHotItem = -1;
7515 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7516 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7517 infoPtr->hwndEdit = 0;
7518 infoPtr->bEditing = FALSE;
7519 infoPtr->nEditLabelItem = -1;
7520 infoPtr->bIsDrawing = FALSE;
7522 /* get default font (icon title) */
7523 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7524 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7525 infoPtr->hFont = infoPtr->hDefaultFont;
7526 LISTVIEW_SaveTextMetrics(infoPtr);
7528 /* create header */
7529 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7530 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7531 0, 0, 0, 0, hwnd, (HMENU)0,
7532 lpcs->hInstance, NULL);
7534 /* set header unicode format */
7535 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
7537 /* set header font */
7538 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7539 (LPARAM)TRUE);
7541 if (uView == LVS_ICON)
7543 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7544 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7546 else if (uView == LVS_REPORT)
7548 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7550 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7552 else
7554 /* set HDS_HIDDEN flag to hide the header bar */
7555 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7556 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7560 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7561 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7563 else
7565 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7566 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7569 /* display unsupported listview window styles */
7570 LISTVIEW_UnsupportedStyles(lpcs->style);
7572 /* allocate memory for the data structure */
7573 infoPtr->hdpaItems = DPA_Create(10);
7575 /* allocate memory for the selection ranges */
7576 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7578 /* initialize size of items */
7579 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
7580 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7582 /* initialize the hover time to -1(indicating the default system hover time) */
7583 infoPtr->dwHoverTime = -1;
7585 return 0;
7588 /***
7589 * DESCRIPTION:
7590 * Erases the background of the listview control.
7592 * PARAMETER(S):
7593 * [I] infoPtr : valid pointer to the listview structure
7594 * [I] WPARAM : device context handle
7595 * [I] LPARAM : not used
7597 * RETURN:
7598 * SUCCESS : TRUE
7599 * FAILURE : FALSE
7601 static LRESULT LISTVIEW_EraseBackground(LISTVIEW_INFO *infoPtr, WPARAM wParam,
7602 LPARAM lParam)
7604 RECT rc;
7606 TRACE("(wParam=%x, lParam=%lx)\n", wParam, lParam);
7608 if (infoPtr->clrBk == CLR_NONE)
7609 return SendMessageW(GetParent(infoPtr->hwndSelf), WM_ERASEBKGND, wParam, lParam);
7611 GetClientRect(infoPtr->hwndSelf, &rc);
7612 FillRect((HDC)wParam, &rc, infoPtr->hBkBrush);
7614 return 0;
7617 /***
7618 * DESCRIPTION:
7619 * Helper function for LISTVIEW_[HV]Scroll *only*.
7620 * Performs vertical/horizontal scrolling by a give amount.
7622 * PARAMETER(S):
7623 * [I] infoPtr : valid pointer to the listview structure
7624 * [I] dx : amount of horizontal scroll
7625 * [I] dy : amount of vertical scroll
7627 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7629 /* if we have a focus rectagle, we have to erase it before we scroll */
7630 if (infoPtr->bFocus) LISTVIEW_ToggleFocusRect(infoPtr);
7632 /* now we can scroll the list */
7633 ScrollWindowEx( infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7634 &infoPtr->rcList, 0, 0, SW_INVALIDATE);
7636 /* if we have focus, adjust rect, and redraw the rectangle */
7637 if (infoPtr->bFocus)
7639 OffsetRect(&infoPtr->rcFocus, dx, dy);
7640 LISTVIEW_ToggleFocusRect(infoPtr);
7644 /***
7645 * DESCRIPTION:
7646 * Performs vertical scrolling.
7648 * PARAMETER(S):
7649 * [I] infoPtr : valid pointer to the listview structure
7650 * [I] nScrollCode : scroll code
7651 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7652 * [I] hScrollWnd : scrollbar control window handle
7654 * RETURN:
7655 * Zero
7657 * NOTES:
7658 * SB_LINEUP/SB_LINEDOWN:
7659 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7660 * for LVS_REPORT is 1 line
7661 * for LVS_LIST cannot occur
7664 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7665 INT nScrollDiff, HWND hScrollWnd)
7667 UINT uView = LISTVIEW_GetType(infoPtr);
7668 INT nOldScrollPos, nNewScrollPos;
7669 SCROLLINFO scrollInfo;
7670 BOOL is_an_icon;
7672 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7674 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7676 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7677 scrollInfo.cbSize = sizeof(SCROLLINFO);
7678 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7680 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7682 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7684 nOldScrollPos = scrollInfo.nPos;
7685 switch (nScrollCode)
7687 case SB_INTERNAL:
7688 break;
7690 case SB_LINEUP:
7691 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7692 break;
7694 case SB_LINEDOWN:
7695 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7696 break;
7698 case SB_PAGEUP:
7699 nScrollDiff = -scrollInfo.nPage;
7700 break;
7702 case SB_PAGEDOWN:
7703 nScrollDiff = scrollInfo.nPage;
7704 break;
7706 case SB_THUMBPOSITION:
7707 case SB_THUMBTRACK:
7708 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7709 break;
7711 default:
7712 nScrollDiff = 0;
7715 /* quit right away if pos isn't changing */
7716 if (nScrollDiff == 0) return 0;
7718 /* calculate new position, and handle overflows */
7719 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7720 if (nScrollDiff > 0) {
7721 if (nNewScrollPos < nOldScrollPos ||
7722 nNewScrollPos > scrollInfo.nMax)
7723 nNewScrollPos = scrollInfo.nMax;
7724 } else {
7725 if (nNewScrollPos > nOldScrollPos ||
7726 nNewScrollPos < scrollInfo.nMin)
7727 nNewScrollPos = scrollInfo.nMin;
7730 /* set the new position, and reread in case it changed */
7731 scrollInfo.fMask = SIF_POS;
7732 scrollInfo.nPos = nNewScrollPos;
7733 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7735 /* carry on only if it really changed */
7736 if (nNewScrollPos == nOldScrollPos) return 0;
7738 /* now adjust to client coordinates */
7739 nScrollDiff = nOldScrollPos - nNewScrollPos;
7740 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7742 /* and scroll the window */
7743 scroll_list(infoPtr, 0, nScrollDiff);
7745 return 0;
7748 /***
7749 * DESCRIPTION:
7750 * Performs horizontal scrolling.
7752 * PARAMETER(S):
7753 * [I] infoPtr : valid pointer to the listview structure
7754 * [I] nScrollCode : scroll code
7755 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7756 * [I] hScrollWnd : scrollbar control window handle
7758 * RETURN:
7759 * Zero
7761 * NOTES:
7762 * SB_LINELEFT/SB_LINERIGHT:
7763 * for LVS_ICON, LVS_SMALLICON 1 pixel
7764 * for LVS_REPORT is 1 pixel
7765 * for LVS_LIST is 1 column --> which is a 1 because the
7766 * scroll is based on columns not pixels
7769 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7770 INT nScrollDiff, HWND hScrollWnd)
7772 UINT uView = LISTVIEW_GetType(infoPtr);
7773 INT nOldScrollPos, nNewScrollPos;
7774 SCROLLINFO scrollInfo;
7776 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7778 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7780 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7781 scrollInfo.cbSize = sizeof(SCROLLINFO);
7782 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7784 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7786 nOldScrollPos = scrollInfo.nPos;
7788 switch (nScrollCode)
7790 case SB_INTERNAL:
7791 break;
7793 case SB_LINELEFT:
7794 nScrollDiff = -1;
7795 break;
7797 case SB_LINERIGHT:
7798 nScrollDiff = 1;
7799 break;
7801 case SB_PAGELEFT:
7802 nScrollDiff = -scrollInfo.nPage;
7803 break;
7805 case SB_PAGERIGHT:
7806 nScrollDiff = scrollInfo.nPage;
7807 break;
7809 case SB_THUMBPOSITION:
7810 case SB_THUMBTRACK:
7811 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7812 break;
7814 default:
7815 nScrollDiff = 0;
7818 /* quit right away if pos isn't changing */
7819 if (nScrollDiff == 0) return 0;
7821 /* calculate new position, and handle overflows */
7822 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7823 if (nScrollDiff > 0) {
7824 if (nNewScrollPos < nOldScrollPos ||
7825 nNewScrollPos > scrollInfo.nMax)
7826 nNewScrollPos = scrollInfo.nMax;
7827 } else {
7828 if (nNewScrollPos > nOldScrollPos ||
7829 nNewScrollPos < scrollInfo.nMin)
7830 nNewScrollPos = scrollInfo.nMin;
7833 /* set the new position, and reread in case it changed */
7834 scrollInfo.fMask = SIF_POS;
7835 scrollInfo.nPos = nNewScrollPos;
7836 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7838 /* carry on only if it really changed */
7839 if (nNewScrollPos == nOldScrollPos) return 0;
7841 if(uView == LVS_REPORT)
7842 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7844 /* now adjust to client coordinates */
7845 nScrollDiff = nOldScrollPos - nNewScrollPos;
7846 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7848 /* and scroll the window */
7849 scroll_list(infoPtr, nScrollDiff, 0);
7851 return 0;
7854 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7856 UINT uView = LISTVIEW_GetType(infoPtr);
7857 INT gcWheelDelta = 0;
7858 UINT pulScrollLines = 3;
7859 SCROLLINFO scrollInfo;
7861 TRACE("(wheelDelta=%d)\n", wheelDelta);
7863 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7864 gcWheelDelta -= wheelDelta;
7866 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7867 scrollInfo.cbSize = sizeof(SCROLLINFO);
7868 scrollInfo.fMask = SIF_POS | SIF_RANGE;
7870 switch(uView)
7872 case LVS_ICON:
7873 case LVS_SMALLICON:
7875 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7876 * should be fixed in the future.
7878 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7879 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7880 scrollInfo.nPos + (gcWheelDelta < 0) ?
7881 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7882 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7883 break;
7885 case LVS_REPORT:
7886 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7888 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7890 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7891 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7892 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7895 break;
7897 case LVS_LIST:
7898 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7899 break;
7901 return 0;
7904 /***
7905 * DESCRIPTION:
7906 * ???
7908 * PARAMETER(S):
7909 * [I] infoPtr : valid pointer to the listview structure
7910 * [I] INT : virtual key
7911 * [I] LONG : key data
7913 * RETURN:
7914 * Zero
7916 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7918 UINT uView = LISTVIEW_GetType(infoPtr);
7919 INT nItem = -1;
7920 NMLVKEYDOWN nmKeyDown;
7922 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7924 /* send LVN_KEYDOWN notification */
7925 nmKeyDown.wVKey = nVirtualKey;
7926 nmKeyDown.flags = 0;
7927 notify(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7929 switch (nVirtualKey)
7931 case VK_RETURN:
7932 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7934 notify_return(infoPtr);
7935 notify_itemactivate(infoPtr);
7937 break;
7939 case VK_HOME:
7940 if (GETITEMCOUNT(infoPtr) > 0)
7941 nItem = 0;
7942 break;
7944 case VK_END:
7945 if (GETITEMCOUNT(infoPtr) > 0)
7946 nItem = GETITEMCOUNT(infoPtr) - 1;
7947 break;
7949 case VK_LEFT:
7950 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7951 break;
7953 case VK_UP:
7954 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7955 break;
7957 case VK_RIGHT:
7958 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7959 break;
7961 case VK_DOWN:
7962 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7963 break;
7965 case VK_PRIOR:
7966 if (uView == LVS_REPORT)
7967 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7968 else
7969 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7970 * LISTVIEW_GetCountPerRow(infoPtr);
7971 if(nItem < 0) nItem = 0;
7972 break;
7974 case VK_NEXT:
7975 if (uView == LVS_REPORT)
7976 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7977 else
7978 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7979 * LISTVIEW_GetCountPerRow(infoPtr);
7980 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
7981 break;
7984 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7985 LISTVIEW_KeySelection(infoPtr, nItem);
7987 return 0;
7990 /***
7991 * DESCRIPTION:
7992 * Kills the focus.
7994 * PARAMETER(S):
7995 * [I] infoPtr : valid pointer to the listview structure
7997 * RETURN:
7998 * Zero
8000 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8002 TRACE("()\n");
8004 /* if we did not have the focus, there's nothing to do */
8005 if (!infoPtr->bFocus) return 0;
8007 /* send NM_KILLFOCUS notification */
8008 notify_killfocus(infoPtr);
8010 /* if we have a focus rectagle, get rid of it */
8011 LISTVIEW_ToggleFocusRect(infoPtr);
8013 /* set window focus flag */
8014 infoPtr->bFocus = FALSE;
8016 /* redraw the selected items */
8017 LISTVIEW_InvalidateSelectedItems(infoPtr);
8019 return 0;
8022 /***
8023 * DESCRIPTION:
8024 * Processes double click messages (left mouse button).
8026 * PARAMETER(S):
8027 * [I] infoPtr : valid pointer to the listview structure
8028 * [I] wKey : key flag
8029 * [I] pts : mouse coordinate
8031 * RETURN:
8032 * Zero
8034 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8036 LVHITTESTINFO htInfo;
8037 NMLISTVIEW nmlv;
8039 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8041 htInfo.pt.x = pts.x;
8042 htInfo.pt.y = pts.y;
8044 /* send NM_DBLCLK notification */
8045 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8046 if (LISTVIEW_HitTestItem(infoPtr, &htInfo, TRUE) != -1)
8048 nmlv.iItem = htInfo.iItem;
8049 nmlv.iSubItem = htInfo.iSubItem;
8051 else
8053 nmlv.iItem = -1;
8054 nmlv.iSubItem = 0;
8056 nmlv.ptAction.x = pts.x;
8057 nmlv.ptAction.y = pts.y;
8058 listview_notify(infoPtr, NM_DBLCLK, &nmlv);
8061 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8062 if(nmlv.iItem != -1)
8063 notify_itemactivate(infoPtr);
8065 return 0;
8068 /***
8069 * DESCRIPTION:
8070 * Processes mouse down messages (left mouse button).
8072 * PARAMETER(S):
8073 * [I] infoPtr : valid pointer to the listview structure
8074 * [I] wKey : key flag
8075 * [I] pts : mouse coordinate
8077 * RETURN:
8078 * Zero
8080 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8082 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
8083 static BOOL bGroupSelect = TRUE;
8084 POINT pt = { pts.x, pts.y };
8085 INT nItem;
8087 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8089 /* send NM_RELEASEDCAPTURE notification */
8090 notify_releasedcapture(infoPtr);
8092 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8094 /* set left button down flag */
8095 infoPtr->bLButtonDown = TRUE;
8097 nItem = LISTVIEW_GetItemAtPt(infoPtr, pt);
8098 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8100 if (lStyle & LVS_SINGLESEL)
8102 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8103 && infoPtr->nEditLabelItem == -1)
8104 infoPtr->nEditLabelItem = nItem;
8105 else
8106 LISTVIEW_SetSelection(infoPtr, nItem);
8108 else
8110 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8112 if (bGroupSelect)
8113 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8114 else
8115 LISTVIEW_AddSelection(infoPtr, nItem);
8117 else if (wKey & MK_CONTROL)
8119 LVITEMW item;
8121 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8123 ZeroMemory(&item, sizeof(item));
8124 item.stateMask = LVIS_SELECTED;
8125 if(bGroupSelect) item.state = LVIS_SELECTED;
8126 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8128 LISTVIEW_SetItemFocus(infoPtr, nItem);
8129 infoPtr->nSelectionMark = nItem;
8131 else if (wKey & MK_SHIFT)
8133 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8135 else
8137 BOOL was_selected =
8138 (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8140 /* set selection (clears other pre-existing selections) */
8141 LISTVIEW_SetSelection(infoPtr, nItem);
8143 if (was_selected && infoPtr->nEditLabelItem == -1)
8144 infoPtr->nEditLabelItem = nItem;
8148 else
8150 /* remove all selections */
8151 LISTVIEW_RemoveAllSelections(infoPtr);
8154 /* redraw if we could have possibly selected something */
8155 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8157 return 0;
8160 /***
8161 * DESCRIPTION:
8162 * Processes mouse up messages (left mouse button).
8164 * PARAMETER(S):
8165 * [I] infoPtr : valid pointer to the listview structure
8166 * [I] wKey : key flag
8167 * [I] pts : mouse coordinate
8169 * RETURN:
8170 * Zero
8172 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8174 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8176 if (infoPtr->bLButtonDown)
8178 LVHITTESTINFO lvHitTestInfo;
8179 NMLISTVIEW nmlv;
8181 lvHitTestInfo.pt.x = pts.x;
8182 lvHitTestInfo.pt.y = pts.y;
8184 /* send NM_CLICK notification */
8185 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8186 if (LISTVIEW_HitTestItem(infoPtr, &lvHitTestInfo, TRUE) != -1)
8188 nmlv.iItem = lvHitTestInfo.iItem;
8189 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8191 else
8193 nmlv.iItem = -1;
8194 nmlv.iSubItem = 0;
8196 nmlv.ptAction.x = pts.x;
8197 nmlv.ptAction.y = pts.y;
8198 listview_notify(infoPtr, NM_CLICK, &nmlv);
8200 /* set left button flag */
8201 infoPtr->bLButtonDown = FALSE;
8203 if(infoPtr->nEditLabelItem != -1)
8205 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL) {
8206 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8208 infoPtr->nEditLabelItem = -1;
8212 return 0;
8215 /***
8216 * DESCRIPTION:
8217 * Destroys the listview control (called after WM_DESTROY).
8219 * PARAMETER(S):
8220 * [I] infoPtr : valid pointer to the listview structure
8222 * RETURN:
8223 * Zero
8225 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8227 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
8229 TRACE("()\n");
8231 /* delete all items */
8232 LISTVIEW_DeleteAllItems(infoPtr);
8234 /* destroy data structure */
8235 DPA_Destroy(infoPtr->hdpaItems);
8236 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8238 /* destroy image lists */
8239 if (!(lStyle & LVS_SHAREIMAGELISTS))
8241 #if 0
8242 /* FIXME: If the caller does a ImageList_Destroy and then we
8243 * do this code the area will be freed twice. Currently
8244 * this generates an "err:heap:HEAP_ValidateInUseArena
8245 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
8246 * has PREV_FREE flag" sometimes.
8248 * We will leak the memory till we figure out how to fix
8250 if (infoPtr->himlNormal)
8251 ImageList_Destroy(infoPtr->himlNormal);
8252 if (infoPtr->himlSmall)
8253 ImageList_Destroy(infoPtr->himlSmall);
8254 if (infoPtr->himlState)
8255 ImageList_Destroy(infoPtr->himlState);
8256 #endif
8259 /* destroy font, bkgnd brush */
8260 infoPtr->hFont = 0;
8261 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8262 if (infoPtr->hBkBrush) DeleteObject(infoPtr->hBkBrush);
8264 /* free listview info pointer*/
8265 COMCTL32_Free(infoPtr);
8267 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
8268 return 0;
8271 /***
8272 * DESCRIPTION:
8273 * Handles notifications from children.
8275 * PARAMETER(S):
8276 * [I] infoPtr : valid pointer to the listview structure
8277 * [I] INT : control identifier
8278 * [I] LPNMHDR : notification information
8280 * RETURN:
8281 * Zero
8283 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
8285 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
8287 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8289 /* handle notification from header control */
8290 if (lpnmh->code == HDN_ENDTRACKW)
8292 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8293 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8295 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
8297 /* Handle sorting by Header Column */
8298 NMLISTVIEW nmlv;
8300 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8301 nmlv.iItem = -1;
8302 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8303 listview_notify(infoPtr, LVN_COLUMNCLICK, &nmlv);
8305 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8307 /* Idealy this should be done in HDN_ENDTRACKA
8308 * but since SetItemBounds in Header.c is called after
8309 * the notification is sent, it is neccessary to handle the
8310 * update of the scroll bar here (Header.c works fine as it is,
8311 * no need to disturb it)
8313 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8314 LISTVIEW_UpdateScroll(infoPtr);
8315 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8320 return 0;
8323 /***
8324 * DESCRIPTION:
8325 * Determines the type of structure to use.
8327 * PARAMETER(S):
8328 * [I] infoPtr : valid pointer to the listview structureof the sender
8329 * [I] HWND : listview window handle
8330 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8332 * RETURN:
8333 * Zero
8335 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8337 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
8339 if (nCommand == NF_REQUERY)
8340 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8341 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8342 return 0;
8345 /***
8346 * DESCRIPTION:
8347 * Paints/Repaints the listview control.
8349 * PARAMETER(S):
8350 * [I] infoPtr : valid pointer to the listview structure
8351 * [I] HDC : device context handle
8353 * RETURN:
8354 * Zero
8356 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8358 PAINTSTRUCT ps;
8360 TRACE("(hdc=%x)\n", hdc);
8362 if (hdc == 0)
8364 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8365 TRACE("ps erase=%s, rect=(%d,%d)-(%d,%d)\n",
8366 ps.fErase ? "TRUE" : "FALSE",
8367 ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
8368 if (ps.fErase)
8369 LISTVIEW_EraseBackground(infoPtr, (WPARAM)hdc, 0);
8370 LISTVIEW_Refresh(infoPtr, hdc);
8371 EndPaint(infoPtr->hwndSelf, &ps);
8373 else
8375 LISTVIEW_Refresh(infoPtr, hdc);
8378 return 0;
8381 /***
8382 * DESCRIPTION:
8383 * Processes double click messages (right mouse button).
8385 * PARAMETER(S):
8386 * [I] infoPtr : valid pointer to the listview structure
8387 * [I] wKey : key flag
8388 * [I] pts : mouse coordinate
8390 * RETURN:
8391 * Zero
8393 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8395 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8397 /* send NM_RELEASEDCAPTURE notification */
8398 notify_releasedcapture(infoPtr);
8400 /* send NM_RDBLCLK notification */
8401 notify_rdblclk(infoPtr);
8403 return 0;
8406 /***
8407 * DESCRIPTION:
8408 * Processes mouse down messages (right mouse button).
8410 * PARAMETER(S):
8411 * [I] infoPtr : valid pointer to the listview structure
8412 * [I] wKey : key flag
8413 * [I] pts : mouse coordinate
8415 * RETURN:
8416 * Zero
8418 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8420 INT nItem;
8421 NMLISTVIEW nmlv;
8422 LVHITTESTINFO lvHitTestInfo;
8423 POINT pt = { pts.x, pts.y };
8425 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8427 /* send NM_RELEASEDCAPTURE notification */
8428 notify_releasedcapture(infoPtr);
8430 /* make sure the listview control window has the focus */
8431 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8433 /* set right button down flag */
8434 infoPtr->bRButtonDown = TRUE;
8436 /* determine the index of the selected item */
8437 nItem = LISTVIEW_GetItemAtPt(infoPtr, pt);
8438 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8440 LISTVIEW_SetItemFocus(infoPtr,nItem);
8441 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8442 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8443 LISTVIEW_SetSelection(infoPtr, nItem);
8445 else
8447 LISTVIEW_RemoveAllSelections(infoPtr);
8450 lvHitTestInfo.pt.x = pts.x;
8451 lvHitTestInfo.pt.y = pts.y;
8453 /* Send NM_RClICK notification */
8454 ZeroMemory(&nmlv, sizeof(nmlv));
8455 if (LISTVIEW_HitTestItem(infoPtr, &lvHitTestInfo, TRUE) != -1)
8457 nmlv.iItem = lvHitTestInfo.iItem;
8458 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8460 else
8462 nmlv.iItem = -1;
8463 nmlv.iSubItem = 0;
8465 nmlv.ptAction.x = pts.x;
8466 nmlv.ptAction.y = pts.y;
8467 listview_notify(infoPtr, NM_RCLICK, &nmlv);
8469 return 0;
8472 /***
8473 * DESCRIPTION:
8474 * Processes mouse up messages (right mouse button).
8476 * PARAMETER(S):
8477 * [I] infoPtr : valid pointer to the listview structure
8478 * [I] wKey : key flag
8479 * [I] pts : mouse coordinate
8481 * RETURN:
8482 * Zero
8484 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8486 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8488 if (infoPtr->bRButtonDown)
8490 POINT pt = { pts.x, pts.y };
8492 /* set button flag */
8493 infoPtr->bRButtonDown = FALSE;
8495 /* Change to screen coordinate for WM_CONTEXTMENU */
8496 ClientToScreen(infoPtr->hwndSelf, &pt);
8498 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8499 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8500 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8503 return 0;
8507 /***
8508 * DESCRIPTION:
8509 * Sets the cursor.
8511 * PARAMETER(S):
8512 * [I] infoPtr : valid pointer to the listview structure
8513 * [I] hwnd : window handle of window containing the cursor
8514 * [I] nHittest : hit-test code
8515 * [I] wMouseMsg : ideintifier of the mouse message
8517 * RETURN:
8518 * TRUE if cursor is set
8519 * FALSE otherwise
8521 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8523 POINT pt;
8525 if(!(infoPtr->dwExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8527 if(!infoPtr->hHotCursor) return FALSE;
8529 GetCursorPos(&pt);
8530 if (LISTVIEW_GetItemAtPt(infoPtr, pt) < 0) return FALSE;
8532 SetCursor(infoPtr->hHotCursor);
8534 return TRUE;
8537 /***
8538 * DESCRIPTION:
8539 * Sets the focus.
8541 * PARAMETER(S):
8542 * [I] infoPtr : valid pointer to the listview structure
8543 * [I] infoPtr : handle of previously focused window
8545 * RETURN:
8546 * Zero
8548 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8550 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
8552 /* if we have the focus already, there's nothing to do */
8553 if (infoPtr->bFocus) return 0;
8555 /* send NM_SETFOCUS notification */
8556 notify_setfocus(infoPtr);
8558 /* put the focus rect back on */
8559 LISTVIEW_ToggleFocusRect(infoPtr);
8561 /* set window focus flag */
8562 infoPtr->bFocus = TRUE;
8564 /* redraw all visible selected items */
8565 LISTVIEW_InvalidateSelectedItems(infoPtr);
8567 return 0;
8570 /***
8571 * DESCRIPTION:
8572 * Sets the font.
8574 * PARAMETER(S):
8575 * [I] infoPtr : valid pointer to the listview structure
8576 * [I] HFONT : font handle
8577 * [I] WORD : redraw flag
8579 * RETURN:
8580 * Zero
8582 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8584 UINT uView = LISTVIEW_GetType(infoPtr);
8586 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
8588 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8589 LISTVIEW_SaveTextMetrics(infoPtr);
8591 if (uView == LVS_REPORT)
8593 /* set header font */
8594 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
8595 MAKELPARAM(fRedraw, 0));
8598 /* invalidate listview control client area */
8599 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8601 if (fRedraw) UpdateWindow(infoPtr->hwndSelf);
8603 return 0;
8606 /***
8607 * DESCRIPTION:
8608 * Message handling for WM_SETREDRAW.
8609 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8611 * PARAMETER(S):
8612 * [I] infoPtr : valid pointer to the listview structure
8613 * [I] bRedraw: state of redraw flag
8615 * RETURN:
8616 * DefWinProc return value
8618 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8620 LRESULT lResult = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, bRedraw, 0);
8621 if(bRedraw)
8622 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8623 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8624 return lResult;
8627 /***
8628 * DESCRIPTION:
8629 * Resizes the listview control. This function processes WM_SIZE
8630 * messages. At this time, the width and height are not used.
8632 * PARAMETER(S):
8633 * [I] infoPtr : valid pointer to the listview structure
8634 * [I] WORD : new width
8635 * [I] WORD : new height
8637 * RETURN:
8638 * Zero
8640 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8642 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
8643 UINT uView = lStyle & LVS_TYPEMASK;
8645 TRACE("(width=%d, height=%d)\n", Width, Height);
8647 if (LISTVIEW_UpdateSize(infoPtr))
8649 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8651 if (lStyle & LVS_ALIGNLEFT)
8652 LISTVIEW_AlignLeft(infoPtr);
8653 else
8654 LISTVIEW_AlignTop(infoPtr);
8657 LISTVIEW_UpdateScroll(infoPtr);
8659 /* invalidate client area + erase background */
8660 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8663 return 0;
8666 /***
8667 * DESCRIPTION:
8668 * Sets the size information.
8670 * PARAMETER(S):
8671 * [I] infoPtr : valid pointer to the listview structure
8673 * RETURN:
8674 * Zero if no size change
8675 * 1 of size changed
8677 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8679 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
8680 UINT uView = lStyle & LVS_TYPEMASK;
8681 RECT rcList;
8682 RECT rcOld;
8684 GetClientRect(infoPtr->hwndSelf, &rcList);
8685 CopyRect(&rcOld,&(infoPtr->rcList));
8686 infoPtr->rcList.left = 0;
8687 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8688 infoPtr->rcList.top = 0;
8689 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8691 if (uView == LVS_LIST)
8693 /* Apparently the "LIST" style is supposed to have the same
8694 * number of items in a column even if there is no scroll bar.
8695 * Since if a scroll bar already exists then the bottom is already
8696 * reduced, only reduce if the scroll bar does not currently exist.
8697 * The "2" is there to mimic the native control. I think it may be
8698 * related to either padding or edges. (GLA 7/2002)
8700 if (!(lStyle & WS_HSCROLL))
8702 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8703 if (infoPtr->rcList.bottom > nHScrollHeight)
8704 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8706 else
8708 if (infoPtr->rcList.bottom > 2)
8709 infoPtr->rcList.bottom -= 2;
8712 else if (uView == LVS_REPORT)
8714 HDLAYOUT hl;
8715 WINDOWPOS wp;
8717 hl.prc = &rcList;
8718 hl.pwpos = &wp;
8719 Header_Layout(infoPtr->hwndHeader, &hl);
8721 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8723 if (!(LVS_NOCOLUMNHEADER & lStyle))
8724 infoPtr->rcList.top = max(wp.cy, 0);
8726 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8729 /***
8730 * DESCRIPTION:
8731 * Processes WM_STYLECHANGED messages.
8733 * PARAMETER(S):
8734 * [I] infoPtr : valid pointer to the listview structure
8735 * [I] WPARAM : window style type (normal or extended)
8736 * [I] LPSTYLESTRUCT : window style information
8738 * RETURN:
8739 * Zero
8741 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8742 LPSTYLESTRUCT lpss)
8744 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8745 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8746 RECT rcList = infoPtr->rcList;
8748 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8749 wStyleType, lpss->styleOld, lpss->styleNew);
8751 if (wStyleType == GWL_STYLE)
8753 if (uOldView == LVS_REPORT)
8754 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8756 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8757 ((lpss->styleNew & WS_HSCROLL) == 0))
8758 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8760 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8761 ((lpss->styleNew & WS_VSCROLL) == 0))
8762 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8764 /* If switching modes, then start with no scroll bars and then
8765 * decide.
8767 if (uNewView != uOldView)
8768 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8770 if (uNewView == LVS_ICON)
8772 INT oldcx, oldcy;
8774 /* First readjust the iconSize and if necessary the iconSpacing */
8775 oldcx = infoPtr->iconSize.cx;
8776 oldcy = infoPtr->iconSize.cy;
8777 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8778 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8779 if (infoPtr->himlNormal != NULL)
8781 INT cx, cy;
8782 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8783 infoPtr->iconSize.cx = cx;
8784 infoPtr->iconSize.cy = cy;
8786 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8788 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8789 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8790 LISTVIEW_SetIconSpacing(infoPtr,0);
8793 /* Now update the full item width and height */
8794 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8795 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8796 if (lpss->styleNew & LVS_ALIGNLEFT)
8797 LISTVIEW_AlignLeft(infoPtr);
8798 else
8799 LISTVIEW_AlignTop(infoPtr);
8801 else if (uNewView == LVS_REPORT)
8803 HDLAYOUT hl;
8804 WINDOWPOS wp;
8806 hl.prc = &rcList;
8807 hl.pwpos = &wp;
8808 Header_Layout(infoPtr->hwndHeader, &hl);
8809 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8810 wp.flags);
8811 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8812 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8814 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8815 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8816 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8817 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8819 else if (uNewView == LVS_LIST)
8821 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8822 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8823 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8824 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8826 else
8828 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8829 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8830 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8831 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8832 if (lpss->styleNew & LVS_ALIGNLEFT)
8833 LISTVIEW_AlignLeft(infoPtr);
8834 else
8835 LISTVIEW_AlignTop(infoPtr);
8838 /* update the size of the client area */
8839 LISTVIEW_UpdateSize(infoPtr);
8841 /* add scrollbars if needed */
8842 LISTVIEW_UpdateScroll(infoPtr);
8844 /* invalidate client area + erase background */
8845 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8847 /* print the list of unsupported window styles */
8848 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8851 /* If they change the view and we have an active edit control
8852 we will need to kill the control since the redraw will
8853 misplace the edit control.
8855 if (infoPtr->bEditing &&
8856 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8857 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8859 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8862 return 0;
8865 /***
8866 * DESCRIPTION:
8867 * Window procedure of the listview control.
8870 static LRESULT WINAPI
8871 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8873 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8875 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8877 if (!infoPtr && (uMsg != WM_CREATE))
8878 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8880 switch (uMsg)
8882 case LVM_APPROXIMATEVIEWRECT:
8883 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8884 LOWORD(lParam), HIWORD(lParam));
8885 case LVM_ARRANGE:
8886 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8888 /* case LVN_CANCELEDITLABEL */
8890 /* case LVM_CREATEDRAGIMAGE: */
8892 case LVM_DELETEALLITEMS:
8893 return LISTVIEW_DeleteAllItems(infoPtr);
8895 case LVM_DELETECOLUMN:
8896 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8898 case LVM_DELETEITEM:
8899 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8901 case LVM_EDITLABELW:
8902 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8904 case LVM_EDITLABELA:
8905 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8907 /* case LVN_ENABLEGROUPVIEW: */
8909 case LVM_ENSUREVISIBLE:
8910 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8912 case LVM_FINDITEMW:
8913 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8915 case LVM_FINDITEMA:
8916 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8918 case LVM_GETBKCOLOR:
8919 return infoPtr->clrBk;
8921 /* case LVM_GETBKIMAGE: */
8923 case LVM_GETCALLBACKMASK:
8924 return infoPtr->uCallbackMask;
8926 case LVM_GETCOLUMNA:
8927 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8929 case LVM_GETCOLUMNW:
8930 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8932 case LVM_GETCOLUMNORDERARRAY:
8933 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8935 case LVM_GETCOLUMNWIDTH:
8936 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8938 case LVM_GETCOUNTPERPAGE:
8939 return LISTVIEW_GetCountPerPage(infoPtr);
8941 case LVM_GETEDITCONTROL:
8942 return (LRESULT)infoPtr->hwndEdit;
8944 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8945 return infoPtr->dwExStyle;
8947 case LVM_GETHEADER:
8948 return (LRESULT)infoPtr->hwndHeader;
8950 case LVM_GETHOTCURSOR:
8951 return infoPtr->hHotCursor;
8953 case LVM_GETHOTITEM:
8954 return infoPtr->nHotItem;
8956 case LVM_GETHOVERTIME:
8957 return infoPtr->dwHoverTime;
8959 case LVM_GETIMAGELIST:
8960 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8962 /* case LVN_GETINSERTMARK: */
8964 /* case LVN_GETINSERTMARKCOLOR: */
8966 /* case LVN_GETINSERTMARKRECT: */
8968 case LVM_GETISEARCHSTRINGA:
8969 case LVM_GETISEARCHSTRINGW:
8970 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8971 return FALSE;
8973 case LVM_GETITEMA:
8974 return LISTVIEW_GetItemT(infoPtr, (LPLVITEMW)lParam, FALSE, FALSE);
8976 case LVM_GETITEMW:
8977 return LISTVIEW_GetItemT(infoPtr, (LPLVITEMW)lParam, FALSE, TRUE);
8979 case LVM_GETITEMCOUNT:
8980 return GETITEMCOUNT(infoPtr);
8982 case LVM_GETITEMPOSITION:
8983 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8985 case LVM_GETITEMRECT:
8986 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8988 case LVM_GETITEMSPACING:
8989 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8991 case LVM_GETITEMSTATE:
8992 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8994 case LVM_GETITEMTEXTA:
8995 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8997 case LVM_GETITEMTEXTW:
8998 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9000 case LVM_GETNEXTITEM:
9001 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9003 case LVM_GETNUMBEROFWORKAREAS:
9004 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9005 return 1;
9007 case LVM_GETORIGIN:
9008 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9010 /* case LVN_GETOUTLINECOLOR: */
9012 /* case LVM_GETSELECTEDCOLUMN: */
9014 case LVM_GETSELECTEDCOUNT:
9015 return LISTVIEW_GetSelectedCount(infoPtr);
9017 case LVM_GETSELECTIONMARK:
9018 return infoPtr->nSelectionMark;
9020 case LVM_GETSTRINGWIDTHA:
9021 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9023 case LVM_GETSTRINGWIDTHW:
9024 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9026 case LVM_GETSUBITEMRECT:
9027 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, ((LPRECT)lParam)->top,
9028 ((LPRECT)lParam)->left, (LPRECT)lParam);
9030 case LVM_GETTEXTBKCOLOR:
9031 return LISTVIEW_GetTextBkColor(infoPtr);
9033 case LVM_GETTEXTCOLOR:
9034 return LISTVIEW_GetTextColor(infoPtr);
9036 /* case LVN_GETTILEINFO: */
9038 /* case LVN_GETTILEVIEWINFO: */
9040 case LVM_GETTOOLTIPS:
9041 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9042 return FALSE;
9044 case LVM_GETTOPINDEX:
9045 return LISTVIEW_GetTopIndex(infoPtr);
9047 /*case LVM_GETUNICODEFORMAT:
9048 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9049 return FALSE;*/
9051 case LVM_GETVIEWRECT:
9052 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9054 case LVM_GETWORKAREAS:
9055 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9056 return FALSE;
9058 /* case LVN_HASGROUP: */
9060 case LVM_HITTEST:
9061 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam);
9063 case LVM_INSERTCOLUMNA:
9064 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9066 case LVM_INSERTCOLUMNW:
9067 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9069 /* case LVN_INSERTGROUP: */
9071 /* case LVN_INSERTGROUPSORTED: */
9073 case LVM_INSERTITEMA:
9074 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9076 case LVM_INSERTITEMW:
9077 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9079 /* case LVN_INSERTMARKHITTEST: */
9081 /* case LVN_ISGROUPVIEWENABLED: */
9083 /* case LVN_MAPIDTOINDEX: */
9085 /* case LVN_INEDXTOID: */
9087 /* case LVN_MOVEGROUP: */
9089 /* case LVN_MOVEITEMTOGROUP: */
9091 case LVM_REDRAWITEMS:
9092 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9094 /* case LVN_REMOVEALLGROUPS: */
9096 /* case LVN_REMOVEGROUP: */
9098 case LVM_SCROLL:
9099 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9101 case LVM_SETBKCOLOR:
9102 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9104 /* case LVM_SETBKIMAGE: */
9106 case LVM_SETCALLBACKMASK:
9107 infoPtr->uCallbackMask = (UINT)wParam;
9108 return TRUE;
9110 case LVM_SETCOLUMNA:
9111 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9113 case LVM_SETCOLUMNW:
9114 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9116 case LVM_SETCOLUMNORDERARRAY:
9117 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9119 case LVM_SETCOLUMNWIDTH:
9120 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
9122 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9123 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9125 /* case LVN_SETGROUPINFO: */
9127 /* case LVN_SETGROUPMETRICS: */
9129 case LVM_SETHOTCURSOR:
9130 return LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9132 case LVM_SETHOTITEM:
9133 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9135 case LVM_SETHOVERTIME:
9136 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9138 case LVM_SETICONSPACING:
9139 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
9141 case LVM_SETIMAGELIST:
9142 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9144 /* case LVN_SETINFOTIP: */
9146 /* case LVN_SETINSERTMARK: */
9148 /* case LVN_SETINSERTMARKCOLOR: */
9150 case LVM_SETITEMA:
9151 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9153 case LVM_SETITEMW:
9154 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9156 case LVM_SETITEMCOUNT:
9157 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9159 case LVM_SETITEMPOSITION:
9160 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (INT)LOWORD(lParam),
9161 (INT)HIWORD(lParam));
9163 case LVM_SETITEMPOSITION32:
9164 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, ((POINT*)lParam)->x,
9165 ((POINT*)lParam)->y);
9167 case LVM_SETITEMSTATE:
9168 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9170 case LVM_SETITEMTEXTA:
9171 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9173 case LVM_SETITEMTEXTW:
9174 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9176 /* case LVN_SETOUTLINECOLOR: */
9178 /* case LVN_SETSELECTEDCOLUMN: */
9180 case LVM_SETSELECTIONMARK:
9181 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9183 case LVM_SETTEXTBKCOLOR:
9184 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9186 case LVM_SETTEXTCOLOR:
9187 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9189 /* case LVN_SETTILEINFO: */
9191 /* case LVN_SETTILEVIEWINFO: */
9193 /* case LVN_SETTILEWIDTH: */
9195 /* case LVM_SETTOOLTIPS: */
9197 /* case LVM_SETUNICODEFORMAT: */
9199 /* case LVN_SETVIEW: */
9201 /* case LVM_SETWORKAREAS: */
9203 /* case LVN_SORTGROUPS: */
9205 case LVM_SORTITEMS:
9206 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9208 case LVM_SUBITEMHITTEST:
9209 return LISTVIEW_SubItemHitTest(infoPtr, (LPLVHITTESTINFO)lParam);
9211 case LVM_UPDATE:
9212 return LISTVIEW_Update(infoPtr, (INT)wParam);
9214 case WM_CHAR:
9215 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9217 case WM_COMMAND:
9218 return LISTVIEW_Command(infoPtr, wParam, lParam);
9220 case WM_CREATE:
9221 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9223 case WM_ERASEBKGND:
9224 return LISTVIEW_EraseBackground(infoPtr, wParam, lParam);
9226 case WM_GETDLGCODE:
9227 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9229 case WM_GETFONT:
9230 return infoPtr->hFont;
9232 case WM_HSCROLL:
9233 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9235 case WM_KEYDOWN:
9236 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9238 case WM_KILLFOCUS:
9239 return LISTVIEW_KillFocus(infoPtr);
9241 case WM_LBUTTONDBLCLK:
9242 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9244 case WM_LBUTTONDOWN:
9245 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9247 case WM_LBUTTONUP:
9248 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9250 case WM_MOUSEMOVE:
9251 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9253 case WM_MOUSEHOVER:
9254 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9256 case WM_NCDESTROY:
9257 return LISTVIEW_NCDestroy(infoPtr);
9259 case WM_NOTIFY:
9260 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
9262 case WM_NOTIFYFORMAT:
9263 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9265 case WM_PAINT:
9266 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9268 case WM_RBUTTONDBLCLK:
9269 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9271 case WM_RBUTTONDOWN:
9272 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9274 case WM_RBUTTONUP:
9275 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9277 case WM_SETCURSOR:
9278 return LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam));
9280 case WM_SETFOCUS:
9281 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9283 case WM_SETFONT:
9284 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9286 case WM_SETREDRAW:
9287 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9289 case WM_SIZE:
9290 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9292 case WM_STYLECHANGED:
9293 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9295 case WM_SYSCOLORCHANGE:
9296 COMCTL32_RefreshSysColors();
9297 return 0;
9299 /* case WM_TIMER: */
9301 case WM_VSCROLL:
9302 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9304 case WM_MOUSEWHEEL:
9305 if (wParam & (MK_SHIFT | MK_CONTROL))
9306 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9307 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9309 case WM_WINDOWPOSCHANGED:
9310 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
9311 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9312 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9313 LISTVIEW_UpdateSize(infoPtr);
9314 LISTVIEW_UpdateScroll(infoPtr);
9316 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9318 /* case WM_WININICHANGE: */
9320 default:
9321 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9322 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9324 /* call default window procedure */
9325 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9328 return 0;
9331 /***
9332 * DESCRIPTION:
9333 * Registers the window class.
9335 * PARAMETER(S):
9336 * None
9338 * RETURN:
9339 * None
9341 void LISTVIEW_Register(void)
9343 WNDCLASSW wndClass;
9345 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9346 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9347 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9348 wndClass.cbClsExtra = 0;
9349 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9350 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9351 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9352 wndClass.lpszClassName = WC_LISTVIEWW;
9353 RegisterClassW(&wndClass);
9356 /***
9357 * DESCRIPTION:
9358 * Unregisters the window class.
9360 * PARAMETER(S):
9361 * None
9363 * RETURN:
9364 * None
9366 void LISTVIEW_Unregister(void)
9368 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9371 /***
9372 * DESCRIPTION:
9373 * Handle any WM_COMMAND messages
9375 * PARAMETER(S):
9377 * RETURN:
9379 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9381 switch (HIWORD(wParam))
9383 case EN_UPDATE:
9386 * Adjust the edit window size
9388 WCHAR buffer[1024];
9389 HDC hdc = GetDC(infoPtr->hwndEdit);
9390 HFONT hFont, hOldFont = 0;
9391 RECT rect;
9392 SIZE sz;
9393 int len;
9395 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9396 GetWindowRect(infoPtr->hwndEdit, &rect);
9398 /* Select font to get the right dimension of the string */
9399 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9400 if(hFont != 0)
9402 hOldFont = SelectObject(hdc, hFont);
9405 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9407 TEXTMETRICW textMetric;
9409 /* Add Extra spacing for the next character */
9410 GetTextMetricsW(hdc, &textMetric);
9411 sz.cx += (textMetric.tmMaxCharWidth * 2);
9413 SetWindowPos (
9414 infoPtr->hwndEdit,
9415 HWND_TOP,
9418 sz.cx,
9419 rect.bottom - rect.top,
9420 SWP_DRAWFRAME|SWP_NOMOVE);
9422 if(hFont != 0)
9423 SelectObject(hdc, hOldFont);
9425 ReleaseDC(infoPtr->hwndSelf, hdc);
9427 break;
9430 default:
9431 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
9434 return 0;
9438 /***
9439 * DESCRIPTION:
9440 * Subclassed edit control windproc function
9442 * PARAMETER(S):
9444 * RETURN:
9446 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9447 WPARAM wParam, LPARAM lParam, BOOL isW)
9449 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9450 static BOOL bIgnoreKillFocus = FALSE;
9451 BOOL cancel = FALSE;
9453 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9454 hwnd, uMsg, wParam, lParam, isW);
9456 switch (uMsg)
9458 case WM_GETDLGCODE:
9459 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9461 case WM_KILLFOCUS:
9462 if(bIgnoreKillFocus) return TRUE;
9463 break;
9465 case WM_DESTROY:
9467 WNDPROC editProc = infoPtr->EditWndProc;
9468 infoPtr->EditWndProc = 0;
9469 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9470 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9473 case WM_KEYDOWN:
9474 if (VK_ESCAPE == (INT)wParam)
9476 cancel = TRUE;
9477 break;
9479 else if (VK_RETURN == (INT)wParam)
9480 break;
9482 default:
9483 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9486 if (infoPtr->bEditing)
9488 LPWSTR buffer = NULL;
9490 if (!cancel)
9492 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9494 if (len)
9496 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9498 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9499 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9503 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9504 /* eg. Using a messagebox */
9505 bIgnoreKillFocus = TRUE;
9506 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9508 if (buffer) COMCTL32_Free(buffer);
9510 bIgnoreKillFocus = FALSE;
9513 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9514 return TRUE;
9517 /***
9518 * DESCRIPTION:
9519 * Subclassed edit control windproc function
9521 * PARAMETER(S):
9523 * RETURN:
9525 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9527 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9530 /***
9531 * DESCRIPTION:
9532 * Subclassed edit control windproc function
9534 * PARAMETER(S):
9536 * RETURN:
9538 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9540 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9543 /***
9544 * DESCRIPTION:
9545 * Creates a subclassed edit cotrol
9547 * PARAMETER(S):
9549 * RETURN:
9551 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9552 INT x, INT y, INT width, INT height, BOOL isW)
9554 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9555 HWND hedit;
9556 SIZE sz;
9557 HDC hdc;
9558 HDC hOldFont=0;
9559 TEXTMETRICW textMetric;
9560 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9562 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9564 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9565 hdc = GetDC(infoPtr->hwndSelf);
9567 /* Select the font to get appropriate metric dimensions */
9568 if(infoPtr->hFont != 0)
9569 hOldFont = SelectObject(hdc, infoPtr->hFont);
9571 /*Get String Lenght in pixels */
9572 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9574 /*Add Extra spacing for the next character */
9575 GetTextMetricsW(hdc, &textMetric);
9576 sz.cx += (textMetric.tmMaxCharWidth * 2);
9578 if(infoPtr->hFont != 0)
9579 SelectObject(hdc, hOldFont);
9581 ReleaseDC(infoPtr->hwndSelf, hdc);
9582 if (isW)
9583 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9584 else
9585 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9587 if (!hedit) return 0;
9589 infoPtr->EditWndProc = (WNDPROC)
9590 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9591 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9593 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9595 return hedit;