Moved mode setting out of .spec file into Makefile.
[wine/gsoc_dplay.git] / dlls / comctl32 / listview.c
blob9894de00a12f692eb2215651b6281834762239a2
1 /*
2 * Listview control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 Codeweavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * NOTES
25 * Listview control implementation.
27 * TODO:
28 * 1. No horizontal scrolling when header is larger than the client area.
29 * 2. Drawing optimizations.
30 * 3. Hot item handling.
32 * Notifications:
33 * LISTVIEW_Notify : most notifications from children (editbox and header)
35 * Data structure:
36 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
38 * Advanced functionality:
39 * LISTVIEW_GetNumberOfWorkAreas : not implemented
40 * LISTVIEW_GetISearchString : not implemented
41 * LISTVIEW_GetBkImage : not implemented
42 * LISTVIEW_SetBkImage : not implemented
43 * LISTVIEW_GetColumnOrderArray : simple hack only
44 * LISTVIEW_SetColumnOrderArray : simple hack only
45 * LISTVIEW_Arrange : empty stub
46 * LISTVIEW_ApproximateViewRect : incomplete
47 * LISTVIEW_Update : not completed
49 * Known differences in message stream from native control (not known if
50 * these differences cause problems):
51 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
52 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
53 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
54 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
55 * does *not* invoke DefWindowProc
56 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
57 * processing for "USEDOUBLECLICKTIME".
61 #include "config.h"
62 #include "wine/port.h"
64 #include <ctype.h>
65 #include <string.h>
66 #include <stdlib.h>
67 #include <stdio.h>
69 #include "winbase.h"
70 #include "winnt.h"
71 #include "heap.h"
72 #include "commctrl.h"
73 #include "comctl32.h"
74 #include "wine/debug.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(listview);
78 /* Some definitions for inline edit control */
80 typedef struct tagLV_INTHIT
82 LVHITTESTINFO ht;
83 DWORD distance; /* distance to closest item */
84 INT iDistItem; /* item number that is closest */
85 } LV_INTHIT, *LPLV_INTHIT;
88 typedef struct tagLISTVIEW_SUBITEM
90 LPWSTR pszText;
91 INT iImage;
92 INT iSubItem;
93 } LISTVIEW_SUBITEM;
95 typedef struct tagLISTVIEW_ITEM
97 UINT state;
98 LPWSTR pszText;
99 INT iImage;
100 LPARAM lParam;
101 INT iIndent;
102 POINT ptPosition;
104 } LISTVIEW_ITEM;
106 typedef struct tagLISTVIEW_SELECTION
108 DWORD lower;
109 DWORD upper;
110 } LISTVIEW_SELECTION;
112 typedef struct tagLISTVIEW_INFO
114 HWND hwndSelf;
115 COLORREF clrBk;
116 COLORREF clrText;
117 COLORREF clrTextBk;
118 HIMAGELIST himlNormal;
119 HIMAGELIST himlSmall;
120 HIMAGELIST himlState;
121 BOOL bLButtonDown;
122 BOOL bRButtonDown;
123 INT nFocusedItem;
124 HDPA hdpaSelectionRanges;
125 INT nItemHeight;
126 INT nItemWidth;
127 INT nSelectionMark;
128 INT nHotItem;
129 SHORT notifyFormat;
130 RECT rcList;
131 RECT rcView;
132 SIZE iconSize;
133 SIZE iconSpacing;
134 UINT uCallbackMask;
135 HWND hwndHeader;
136 HFONT hDefaultFont;
137 HCURSOR hHotCursor;
138 HFONT hFont;
139 INT ntmHeight; /* from GetTextMetrics from above font */
140 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
141 BOOL bFocus;
142 DWORD dwExStyle; /* extended listview style */
143 HDPA hdpaItems;
144 PFNLVCOMPARE pfnCompare;
145 LPARAM lParamSort;
146 HWND hwndEdit;
147 BOOL bEditing;
148 WNDPROC EditWndProc;
149 INT nEditLabelItem;
150 DWORD dwHoverTime;
151 INT nColumnCount; /* the number of columns in this control */
153 DWORD lastKeyPressTimestamp; /* Added */
154 WPARAM charCode; /* Added */
155 INT nSearchParamLength; /* Added */
156 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
157 BOOL bIsDrawing;
158 } LISTVIEW_INFO;
160 DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO, hwndSelf);
163 * constants
166 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
167 #define SB_INTERNAL_UP -1
168 #define SB_INTERNAL_DOWN -2
169 #define SB_INTERNAL_RIGHT -3
170 #define SB_INTERNAL_LEFT -4
172 /* maximum size of a label */
173 #define DISP_TEXT_SIZE 512
175 /* padding for items in list and small icon display modes */
176 #define WIDTH_PADDING 12
178 /* padding for items in list, report and small icon display modes */
179 #define HEIGHT_PADDING 1
181 /* offset of items in report display mode */
182 #define REPORT_MARGINX 2
184 /* padding for icon in large icon display mode
185 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
186 * that HITTEST will see.
187 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
188 * ICON_TOP_PADDING - sum of the two above.
189 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
190 * LABEL_VERT_OFFSET - between bottom of text and end of box
192 #define ICON_TOP_PADDING_NOTHITABLE 2
193 #define ICON_TOP_PADDING_HITABLE 2
194 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
195 #define ICON_BOTTOM_PADDING 4
196 #define LABEL_VERT_OFFSET 10
198 /* default label width for items in list and small icon display modes */
199 #define DEFAULT_LABEL_WIDTH 40
201 /* default column width for items in list display mode */
202 #define DEFAULT_COLUMN_WIDTH 128
204 /* Size of "line" scroll for V & H scrolls */
205 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
207 /* Padding betwen image and label */
208 #define IMAGE_PADDING 2
210 /* Padding behind the label */
211 #define TRAILING_PADDING 5
213 /* Border for the icon caption */
214 #define CAPTION_BORDER 2
216 * macros
218 /* retrieve the number of items in the listview */
219 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
220 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
224 * forward declarations
226 static LRESULT LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL, BOOL);
227 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *, LPLV_INTHIT, BOOL);
228 static INT LISTVIEW_HitTestItem(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL);
229 static INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *);
230 static INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *);
231 static VOID LISTVIEW_AlignLeft(LISTVIEW_INFO *);
232 static VOID LISTVIEW_AlignTop(LISTVIEW_INFO *);
233 static VOID LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
234 static VOID LISTVIEW_AddSelection(LISTVIEW_INFO *, INT);
235 static BOOL LISTVIEW_AddSubItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
236 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
237 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *);
238 static BOOL LISTVIEW_GetItemBoundBox(LISTVIEW_INFO *, INT, LPRECT);
239 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
240 static LRESULT LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
241 static LRESULT LISTVIEW_GetSubItemRect(LISTVIEW_INFO *, INT, INT, INT, LPRECT);
242 static INT LISTVIEW_GetItemWidth(LISTVIEW_INFO *);
243 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
244 static LRESULT LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
245 static INT LISTVIEW_CalculateWidth(LISTVIEW_INFO *, INT);
246 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
247 static LRESULT LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
248 static BOOL LISTVIEW_InitItemT(LISTVIEW_INFO *, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
249 static BOOL LISTVIEW_InitSubItemT(LISTVIEW_INFO *, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
250 static INT LISTVIEW_MouseSelection(LISTVIEW_INFO *, POINT);
251 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
252 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
253 static VOID LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
254 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
255 static BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *, INT);
256 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, LONG, LONG);
257 static VOID LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
258 static VOID LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
259 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
260 static BOOL LISTVIEW_SetSubItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
261 static LRESULT LISTVIEW_SetViewRect(LISTVIEW_INFO *, LPRECT);
262 static BOOL LISTVIEW_ToggleSelection(LISTVIEW_INFO *, INT);
263 static VOID LISTVIEW_UnsupportedStyles(LONG);
264 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
265 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
266 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
267 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
268 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *, WPARAM, LPARAM);
269 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
270 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
271 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
272 static BOOL LISTVIEW_IsSelected(LISTVIEW_INFO *, INT);
273 static VOID LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *, INT, INT);
274 static void LISTVIEW_FillBackground(LISTVIEW_INFO *, HDC, LPRECT);
275 static void LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *, int, RECT*);
276 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
277 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, SHORT, HWND);
278 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, SHORT, HWND);
279 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
280 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
281 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
283 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
284 #define KEY_DELAY 450
286 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
288 static inline BOOL is_textW(LPCWSTR text)
290 return text != NULL && text != LPSTR_TEXTCALLBACKW;
293 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
295 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
296 return is_textW(text);
299 static inline int textlenT(LPCWSTR text, BOOL isW)
301 return !is_textT(text, isW) ? 0 :
302 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
305 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
307 if (isDestW)
308 if (isSrcW) lstrcpynW(dest, src, max);
309 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
310 else
311 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
312 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
315 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
317 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
320 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
322 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
325 static inline LPCSTR lv_dmp_str(LPCWSTR text, BOOL isW, INT n)
327 return debugstr_tn(text, isW, min(n, textlenT(text, isW)));
330 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
332 LPWSTR wstr = (LPWSTR)text;
334 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
335 if (!isW && text)
337 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
338 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
339 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
341 TRACE(" wstr=%s\n", debugstr_w(wstr));
342 return wstr;
345 static inline void textfreeT(LPWSTR wstr, BOOL isW)
347 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
351 * dest is a pointer to a Unicode string
352 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
354 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
356 LPWSTR pszText = textdupTtoW(src, isW);
357 BOOL bResult = TRUE;
358 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
359 bResult = Str_SetPtrW(dest, pszText);
360 textfreeT(pszText, isW);
361 return bResult;
364 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
365 WPARAM wParam, LPARAM lParam, BOOL isW)
367 if (isW)
368 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
369 else
370 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
373 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
375 pnmh->hwndFrom = infoPtr->hwndSelf;
376 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
377 pnmh->code = code;
378 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
379 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
382 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
384 hwnd_notify(infoPtr->hwndSelf, LVN_ITEMACTIVATE);
387 static inline BOOL listview_notify(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
389 return notify(infoPtr, code, (LPNMHDR)plvnm);
392 static int tabNotification[] = {
393 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
394 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
395 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
396 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
397 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
398 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
402 static int get_ansi_notification(INT unicodeNotificationCode)
404 int *pTabNotif = tabNotification;
405 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
406 if (*pTabNotif) return *pTabNotif;
407 ERR("unknown notification %x\n", unicodeNotificationCode);
408 return unicodeNotificationCode;
412 Send notification. depends on dispinfoW having same
413 structure as dispinfoA.
414 infoPtr : listview struct
415 notificationCode : *Unicode* notification code
416 pdi : dispinfo structure (can be unicode or ansi)
417 isW : TRUE if dispinfo is Unicode
419 static BOOL dispinfo_notifyT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
421 BOOL bResult = FALSE;
422 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
423 INT realNotifCode;
424 INT cchTempBufMax = 0, savCchTextMax = 0;
425 LPWSTR pszTempBuf = NULL, savPszText = NULL;
427 TRACE("(code=%x, pdi=%p, isW=%d)\n", notificationCode, pdi, isW);
428 TRACE(" notifyFormat=%s\n",
429 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
430 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
431 if (infoPtr->notifyFormat == NFR_ANSI)
432 realNotifCode = get_ansi_notification(notificationCode);
433 else
434 realNotifCode = notificationCode;
436 if (is_textT(pdi->item.pszText, isW))
438 if (isW && infoPtr->notifyFormat == NFR_ANSI)
439 convertToAnsi = TRUE;
440 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
441 convertToUnicode = TRUE;
444 if (convertToAnsi || convertToUnicode)
446 TRACE(" we have to convert the text to the correct format\n");
447 if (notificationCode != LVN_GETDISPINFOW)
448 { /* length of existing text */
449 cchTempBufMax = convertToUnicode ?
450 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
451 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
453 else
454 cchTempBufMax = pdi->item.cchTextMax;
456 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
457 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
458 if (!pszTempBuf) return FALSE;
459 if (convertToUnicode)
460 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
461 pszTempBuf, cchTempBufMax);
462 else
463 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
464 cchTempBufMax, NULL, NULL);
465 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
466 savCchTextMax = pdi->item.cchTextMax;
467 savPszText = pdi->item.pszText;
468 pdi->item.pszText = pszTempBuf;
469 pdi->item.cchTextMax = cchTempBufMax;
472 bResult = notify(infoPtr, realNotifCode, (LPNMHDR)pdi);
474 if (convertToUnicode || convertToAnsi)
475 { /* convert back result */
476 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
477 if (convertToUnicode) /* note : pointer can be changed by app ! */
478 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
479 savCchTextMax, NULL, NULL);
480 else
481 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
482 savPszText, savCchTextMax);
483 pdi->item.pszText = savPszText; /* restores our buffer */
484 pdi->item.cchTextMax = savCchTextMax;
485 HeapFree(GetProcessHeap(), 0, pszTempBuf);
487 return bResult;
490 static inline LRESULT LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL internal)
492 return LISTVIEW_GetItemT(infoPtr, lpLVItem, internal, TRUE);
495 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
497 int res;
499 n = min(min(n, strlenW(s1)), strlenW(s2));
500 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
501 return res ? res - 2 : res;
504 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
506 static int index = 0;
507 static char buffers[20][256];
508 char* buf = buffers[index++ % 20];
509 if (lpLVItem == NULL) return "(null)";
510 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
511 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
512 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
513 lpLVItem->state, lpLVItem->stateMask,
514 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
515 lv_dmp_str(lpLVItem->pszText, isW, 80),
516 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
517 lpLVItem->iIndent);
518 return buf;
521 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
523 static int index = 0;
524 static char buffers[20][256];
525 char* buf = buffers[index++ % 20];
526 if (lpColumn == NULL) return "(null)";
527 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
528 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
529 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
530 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
531 lv_dmp_str(lpColumn->pszText, isW, 80): "",
532 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
533 return buf;
536 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
538 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
539 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
540 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
541 iP->nItemHeight, iP->nItemWidth, dwStyle);
542 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
543 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
544 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
545 TRACE("listview %08x at line %d, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld\n",
546 iP->hwndSelf, line, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy,
547 iP->iconSpacing.cx, iP->iconSpacing.cy);
550 static BOOL
551 LISTVIEW_SendCustomDrawNotify (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, HDC hdc,
552 RECT rc)
554 NMLVCUSTOMDRAW nmcdhdr;
555 LPNMCUSTOMDRAW nmcd;
557 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage, hdc);
559 nmcd= & nmcdhdr.nmcd;
560 nmcd->hdr.hwndFrom = infoPtr->hwndSelf;
561 nmcd->hdr.idFrom = GetWindowLongW( infoPtr->hwndSelf, GWL_ID);
562 nmcd->hdr.code = NM_CUSTOMDRAW;
563 nmcd->dwDrawStage= dwDrawStage;
564 nmcd->hdc = hdc;
565 nmcd->rc.left = rc.left;
566 nmcd->rc.right = rc.right;
567 nmcd->rc.bottom = rc.bottom;
568 nmcd->rc.top = rc.top;
569 nmcd->dwItemSpec = 0;
570 nmcd->uItemState = 0;
571 nmcd->lItemlParam= 0;
572 nmcdhdr.clrText = infoPtr->clrText;
573 nmcdhdr.clrTextBk= infoPtr->clrBk;
575 return (BOOL)SendMessageW (GetParent (infoPtr->hwndSelf), WM_NOTIFY,
576 (WPARAM) nmcd->hdr.idFrom, (LPARAM)&nmcdhdr);
579 static BOOL
580 LISTVIEW_SendCustomDrawItemNotify (LISTVIEW_INFO *infoPtr, HDC hdc,
581 UINT iItem, UINT iSubItem,
582 UINT uItemDrawState)
584 NMLVCUSTOMDRAW nmcdhdr;
585 LPNMCUSTOMDRAW nmcd;
586 DWORD dwDrawStage,dwItemSpec;
587 UINT uItemState;
588 INT retval;
589 RECT itemRect;
590 LVITEMW item;
592 ZeroMemory(&item,sizeof(item));
593 item.iItem = iItem;
594 item.mask = LVIF_PARAM;
595 ListView_GetItemW(infoPtr->hwndSelf,&item);
597 dwDrawStage=CDDS_ITEM | uItemDrawState;
598 dwItemSpec=iItem;
599 uItemState=0;
601 if (LISTVIEW_IsSelected(infoPtr,iItem)) uItemState|=CDIS_SELECTED;
602 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
603 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
605 itemRect.left = LVIR_BOUNDS;
606 LISTVIEW_GetItemRect(infoPtr, iItem, &itemRect);
608 nmcd= & nmcdhdr.nmcd;
609 nmcd->hdr.hwndFrom = infoPtr->hwndSelf;
610 nmcd->hdr.idFrom = GetWindowLongW( infoPtr->hwndSelf, GWL_ID);
611 nmcd->hdr.code = NM_CUSTOMDRAW;
612 nmcd->dwDrawStage= dwDrawStage;
613 nmcd->hdc = hdc;
614 nmcd->rc.left = itemRect.left;
615 nmcd->rc.right = itemRect.right;
616 nmcd->rc.bottom = itemRect.bottom;
617 nmcd->rc.top = itemRect.top;
618 nmcd->dwItemSpec = dwItemSpec;
619 nmcd->uItemState = uItemState;
620 nmcd->lItemlParam= item.lParam;
621 nmcdhdr.clrText = infoPtr->clrText;
622 nmcdhdr.clrTextBk= infoPtr->clrBk;
623 nmcdhdr.iSubItem =iSubItem;
625 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
626 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
627 nmcd->uItemState, nmcd->lItemlParam);
629 retval=SendMessageW (GetParent (infoPtr->hwndSelf), WM_NOTIFY,
630 (WPARAM)nmcd->hdr.idFrom, (LPARAM)&nmcdhdr);
632 infoPtr->clrText=nmcdhdr.clrText;
633 infoPtr->clrBk =nmcdhdr.clrTextBk;
634 return (BOOL) retval;
638 /*************************************************************************
639 * LISTVIEW_ProcessLetterKeys
641 * Processes keyboard messages generated by pressing the letter keys
642 * on the keyboard.
643 * What this does is perform a case insensitive search from the
644 * current position with the following quirks:
645 * - If two chars or more are pressed in quick succession we search
646 * for the corresponding string (e.g. 'abc').
647 * - If there is a delay we wipe away the current search string and
648 * restart with just that char.
649 * - If the user keeps pressing the same character, whether slowly or
650 * fast, so that the search string is entirely composed of this
651 * character ('aaaaa' for instance), then we search for first item
652 * that starting with that character.
653 * - If the user types the above character in quick succession, then
654 * we must also search for the corresponding string ('aaaaa'), and
655 * go to that string if there is a match.
657 * PARAMETERS
658 * [I] hwnd : handle to the window
659 * [I] charCode : the character code, the actual character
660 * [I] keyData : key data
662 * RETURNS
664 * Zero.
666 * BUGS
668 * - The current implementation has a list of characters it will
669 * accept and it ignores averything else. In particular it will
670 * ignore accentuated characters which seems to match what
671 * Windows does. But I'm not sure it makes sense to follow
672 * Windows there.
673 * - We don't sound a beep when the search fails.
675 * SEE ALSO
677 * TREEVIEW_ProcessLetterKeys
679 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
681 INT nItem;
682 INT nSize;
683 INT endidx,idx;
684 LVITEMW item;
685 WCHAR buffer[MAX_PATH];
686 DWORD timestamp,elapsed;
688 /* simple parameter checking */
689 if (!charCode || !keyData)
690 return 0;
692 if (!infoPtr)
693 return 0;
695 /* only allow the valid WM_CHARs through */
696 if (!isalnum(charCode) &&
697 charCode != '.' && charCode != '`' && charCode != '!' &&
698 charCode != '@' && charCode != '#' && charCode != '$' &&
699 charCode != '%' && charCode != '^' && charCode != '&' &&
700 charCode != '*' && charCode != '(' && charCode != ')' &&
701 charCode != '-' && charCode != '_' && charCode != '+' &&
702 charCode != '=' && charCode != '\\'&& charCode != ']' &&
703 charCode != '}' && charCode != '[' && charCode != '{' &&
704 charCode != '/' && charCode != '?' && charCode != '>' &&
705 charCode != '<' && charCode != ',' && charCode != '~')
706 return 0;
708 nSize=GETITEMCOUNT(infoPtr);
709 /* if there's one item or less, there is no where to go */
710 if (nSize <= 1)
711 return 0;
713 /* compute how much time elapsed since last keypress */
714 timestamp=GetTickCount();
715 if (timestamp > infoPtr->lastKeyPressTimestamp) {
716 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
717 } else {
718 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
721 /* update the search parameters */
722 infoPtr->lastKeyPressTimestamp=timestamp;
723 if (elapsed < KEY_DELAY) {
724 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
725 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
727 if (infoPtr->charCode != charCode) {
728 infoPtr->charCode=charCode=0;
730 } else {
731 infoPtr->charCode=charCode;
732 infoPtr->szSearchParam[0]=charCode;
733 infoPtr->nSearchParamLength=1;
734 /* Redundant with the 1 char string */
735 charCode=0;
738 /* and search from the current position */
739 nItem=-1;
740 if (infoPtr->nFocusedItem >= 0) {
741 endidx=infoPtr->nFocusedItem;
742 idx=endidx;
743 /* if looking for single character match,
744 * then we must always move forward
746 if (infoPtr->nSearchParamLength == 1)
747 idx++;
748 } else {
749 endidx=nSize;
750 idx=0;
752 do {
753 if (idx == nSize) {
754 if (endidx == nSize)
755 break;
756 idx=0;
759 /* get item */
760 ZeroMemory(&item, sizeof(item));
761 item.mask = LVIF_TEXT;
762 item.iItem = idx;
763 item.iSubItem = 0;
764 item.pszText = buffer;
765 item.cchTextMax = COUNTOF(buffer);
766 ListView_GetItemW(infoPtr->hwndSelf, &item);
768 /* check for a match */
769 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
770 nItem=idx;
771 break;
772 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
773 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
774 /* This would work but we must keep looking for a longer match */
775 nItem=idx;
777 idx++;
778 } while (idx != endidx);
780 if (nItem != -1) {
781 if (LISTVIEW_KeySelection(infoPtr, nItem)) {
782 /* refresh client area */
783 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
784 UpdateWindow(infoPtr->hwndSelf);
788 return 0;
791 /*************************************************************************
792 * LISTVIEW_UpdateHeaderSize [Internal]
794 * Function to resize the header control
796 * PARAMS
797 * hwnd [I] handle to a window
798 * nNewScrollPos [I] Scroll Pos to Set
800 * RETURNS
801 * nothing
803 * NOTES
805 static VOID LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
807 RECT winRect;
808 POINT point[2];
810 GetWindowRect(infoPtr->hwndHeader, &winRect);
811 point[0].x = winRect.left;
812 point[0].y = winRect.top;
813 point[1].x = winRect.right;
814 point[1].y = winRect.bottom;
816 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
817 point[0].x = -nNewScrollPos;
818 point[1].x += nNewScrollPos;
820 SetWindowPos(infoPtr->hwndHeader,0,
821 point[0].x,point[0].y,point[1].x,point[1].y,
822 SWP_NOZORDER | SWP_NOACTIVATE);
825 /***
826 * DESCRIPTION:
827 * Update the scrollbars. This functions should be called whenever
828 * the content, size or view changes.
830 * PARAMETER(S):
831 * [I] infoPtr : valid pointer to the listview structure
833 * RETURN:
834 * None
836 static VOID LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
838 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
839 UINT uView = lStyle & LVS_TYPEMASK;
840 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
841 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
842 SCROLLINFO scrollInfo;
844 if (lStyle & LVS_NOSCROLL) return;
846 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
847 scrollInfo.cbSize = sizeof(SCROLLINFO);
849 if (uView == LVS_LIST)
851 /* update horizontal scrollbar */
853 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
854 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
855 INT nNumOfItems = GETITEMCOUNT(infoPtr);
857 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
858 TRACE("items=%d, perColumn=%d, perRow=%d\n",
859 nNumOfItems, nCountPerColumn, nCountPerRow);
860 if((nNumOfItems % nCountPerColumn) == 0)
862 scrollInfo.nMax--;
864 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
865 scrollInfo.nPage = nCountPerRow;
866 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
867 TRACE("LVS_LIST\n");
868 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
869 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
871 else if (uView == LVS_REPORT)
873 BOOL test;
875 /* update vertical scrollbar */
876 scrollInfo.nMin = 0;
877 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
878 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
879 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
880 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
881 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
882 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
883 scrollInfo.nMax, scrollInfo.nPage, test);
884 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
885 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
887 /* update horizontal scrollbar */
888 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
889 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo) == FALSE
890 || GETITEMCOUNT(infoPtr) == 0)
892 scrollInfo.nPos = 0;
894 scrollInfo.nMin = 0;
895 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
896 scrollInfo.nPage = nListWidth;
897 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
898 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
899 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
900 scrollInfo.nMax, scrollInfo.nPage, test);
901 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
902 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
904 /* Update the Header Control */
905 scrollInfo.fMask = SIF_POS;
906 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
907 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
910 else
912 RECT rcView;
914 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
916 INT nViewWidth = rcView.right - rcView.left;
917 INT nViewHeight = rcView.bottom - rcView.top;
919 /* Update Horizontal Scrollbar */
920 scrollInfo.fMask = SIF_POS;
921 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo) == FALSE
922 || GETITEMCOUNT(infoPtr) == 0)
924 scrollInfo.nPos = 0;
926 scrollInfo.nMax = max(nViewWidth, 0)-1;
927 scrollInfo.nMin = 0;
928 scrollInfo.nPage = nListWidth;
929 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
930 TRACE("LVS_ICON/SMALLICON Horz.\n");
931 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
933 /* Update Vertical Scrollbar */
934 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
935 scrollInfo.fMask = SIF_POS;
936 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo) == FALSE
937 || GETITEMCOUNT(infoPtr) == 0)
939 scrollInfo.nPos = 0;
941 scrollInfo.nMax = max(nViewHeight,0)-1;
942 scrollInfo.nMin = 0;
943 scrollInfo.nPage = nListHeight;
944 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
945 TRACE("LVS_ICON/SMALLICON Vert.\n");
946 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
951 /***
952 * DESCRIPTION:
953 * Prints a message for unsupported window styles.
954 * A kind of TODO list for window styles.
956 * PARAMETER(S):
957 * [I] LONG : window style
959 * RETURN:
960 * None
962 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
964 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
965 FIXME(" LVS_NOSCROLL\n");
967 if (lStyle & LVS_NOLABELWRAP)
968 FIXME(" LVS_NOLABELWRAP\n");
970 if (!(lStyle & LVS_SHAREIMAGELISTS))
971 FIXME(" !LVS_SHAREIMAGELISTS\n");
973 if (lStyle & LVS_SORTASCENDING)
974 FIXME(" LVS_SORTASCENDING\n");
976 if (lStyle & LVS_SORTDESCENDING)
977 FIXME(" LVS_SORTDESCENDING\n");
980 /***
981 * DESCRIPTION:
982 * Aligns the items with the top edge of the window.
984 * PARAMETER(S):
985 * [I] infoPtr : valid pointer to the listview structure
987 * RETURN:
988 * None
990 static VOID LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
992 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
993 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
994 POINT ptItem;
995 RECT rcView;
996 INT i, off_x=0, off_y=0;
998 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1000 /* Since SetItemPosition uses upper-left of icon, and for
1001 style=LVS_ICON the icon is not left adjusted, get the offset */
1002 if (uView == LVS_ICON)
1004 off_y = ICON_TOP_PADDING;
1005 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1007 ptItem.x = off_x;
1008 ptItem.y = off_y;
1009 ZeroMemory(&rcView, sizeof(RECT));
1010 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1011 off_x, off_y,
1012 infoPtr->rcList.left, infoPtr->rcList.right);
1014 if (nListWidth > infoPtr->nItemWidth)
1016 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1018 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1020 ptItem.x = off_x;
1021 ptItem.y += infoPtr->nItemHeight;
1024 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1025 ptItem.x += infoPtr->nItemWidth;
1026 rcView.right = max(rcView.right, ptItem.x);
1029 rcView.right -= off_x;
1030 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1032 else
1034 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1036 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1037 ptItem.y += infoPtr->nItemHeight;
1040 rcView.right = infoPtr->nItemWidth;
1041 rcView.bottom = ptItem.y-off_y;
1044 LISTVIEW_SetViewRect(infoPtr, &rcView);
1048 /***
1049 * DESCRIPTION:
1050 * Aligns the items with the left edge of the window.
1052 * PARAMETER(S):
1053 * [I] infoPtr : valid pointer to the listview structure
1055 * RETURN:
1056 * None
1058 static VOID LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1060 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
1061 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1062 POINT ptItem;
1063 RECT rcView;
1064 INT i, off_x=0, off_y=0;
1066 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1068 /* Since SetItemPosition uses upper-left of icon, and for
1069 style=LVS_ICON the icon is not left adjusted, get the offset */
1070 if (uView == LVS_ICON)
1072 off_y = ICON_TOP_PADDING;
1073 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1075 ptItem.x = off_x;
1076 ptItem.y = off_y;
1077 ZeroMemory(&rcView, sizeof(RECT));
1078 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1080 if (nListHeight > infoPtr->nItemHeight)
1082 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1084 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1086 ptItem.y = off_y;
1087 ptItem.x += infoPtr->nItemWidth;
1090 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1091 ptItem.y += infoPtr->nItemHeight;
1092 rcView.bottom = max(rcView.bottom, ptItem.y);
1095 rcView.right = ptItem.x + infoPtr->nItemWidth;
1097 else
1099 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1101 LISTVIEW_SetItemPosition(infoPtr, i, ptItem.x, ptItem.y);
1102 ptItem.x += infoPtr->nItemWidth;
1105 rcView.bottom = infoPtr->nItemHeight;
1106 rcView.right = ptItem.x;
1109 LISTVIEW_SetViewRect(infoPtr, &rcView);
1113 /***
1114 * DESCRIPTION:
1115 * Set the bounding rectangle of all the items.
1117 * PARAMETER(S):
1118 * [I] infoPtr : valid pointer to the listview structure
1119 * [I] LPRECT : bounding rectangle
1121 * RETURN:
1122 * SUCCESS : TRUE
1123 * FAILURE : FALSE
1125 static LRESULT LISTVIEW_SetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1127 BOOL bResult = FALSE;
1129 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1130 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1132 if (lprcView != NULL)
1134 bResult = TRUE;
1135 infoPtr->rcView.left = lprcView->left;
1136 infoPtr->rcView.top = lprcView->top;
1137 infoPtr->rcView.right = lprcView->right;
1138 infoPtr->rcView.bottom = lprcView->bottom;
1141 return bResult;
1144 /***
1145 * DESCRIPTION:
1146 * Retrieves the bounding rectangle of all the items.
1148 * PARAMETER(S):
1149 * [I] infoPtr : valid pointer to the listview structure
1150 * [O] LPRECT : bounding rectangle
1152 * RETURN:
1153 * SUCCESS : TRUE
1154 * FAILURE : FALSE
1156 static LRESULT LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1158 BOOL bResult = FALSE;
1159 POINT ptOrigin;
1161 TRACE("(lprcView=%p)\n", lprcView);
1163 if (lprcView != NULL)
1165 bResult = LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
1166 if (bResult)
1168 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1169 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1170 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1171 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1174 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1175 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1178 return bResult;
1181 /***
1182 * DESCRIPTION:
1183 * Retrieves the subitem pointer associated with the subitem index.
1185 * PARAMETER(S):
1186 * [I] HDPA : DPA handle for a specific item
1187 * [I] INT : index of subitem
1189 * RETURN:
1190 * SUCCESS : subitem pointer
1191 * FAILURE : NULL
1193 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1194 INT nSubItem)
1196 LISTVIEW_SUBITEM *lpSubItem;
1197 INT i;
1199 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1201 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1202 if (lpSubItem != NULL)
1204 if (lpSubItem->iSubItem == nSubItem)
1206 return lpSubItem;
1211 return NULL;
1214 /***
1215 * DESCRIPTION:
1216 * Calculates the width of an item.
1218 * PARAMETER(S):
1219 * [I] infoPtr : valid pointer to the listview structure
1220 * [I] LONG : window style
1222 * RETURN:
1223 * Returns item width.
1225 static INT LISTVIEW_GetItemWidth(LISTVIEW_INFO *infoPtr)
1227 LONG style = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
1228 UINT uView = style & LVS_TYPEMASK;
1229 INT nHeaderItemCount;
1230 RECT rcHeaderItem;
1231 INT nItemWidth = 0;
1232 INT nLabelWidth;
1233 INT i;
1235 TRACE("()\n");
1237 if (uView == LVS_ICON)
1239 nItemWidth = infoPtr->iconSpacing.cx;
1241 else if (uView == LVS_REPORT)
1243 /* calculate width of header */
1244 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1245 for (i = 0; i < nHeaderItemCount; i++)
1247 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1249 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1253 else /* for LVS_SMALLICON and LVS_LIST */
1255 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1257 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1258 nItemWidth = max(nItemWidth, nLabelWidth);
1261 /* default label size */
1262 if (GETITEMCOUNT(infoPtr) == 0)
1264 nItemWidth = DEFAULT_COLUMN_WIDTH;
1266 else
1268 if (nItemWidth == 0)
1270 nItemWidth = DEFAULT_LABEL_WIDTH;
1272 else
1274 /* add padding */
1275 nItemWidth += WIDTH_PADDING;
1277 if (infoPtr->himlSmall != NULL)
1279 nItemWidth += infoPtr->iconSize.cx;
1282 if (infoPtr->himlState != NULL)
1284 nItemWidth += infoPtr->iconSize.cx;
1286 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1290 if(nItemWidth == 0)
1292 /* nItemWidth Cannot be Zero */
1293 nItemWidth = 1;
1295 return nItemWidth;
1298 /***
1299 * DESCRIPTION:
1300 * Calculates the width of a specific item.
1302 * PARAMETER(S):
1303 * [I] infoPtr : valid pointer to the listview structure
1304 * [I] LPSTR : string
1306 * RETURN:
1307 * Returns the width of an item width a specified string.
1309 static INT LISTVIEW_CalculateWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1311 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
1312 INT nHeaderItemCount;
1313 RECT rcHeaderItem;
1314 INT nItemWidth = 0;
1315 INT i;
1317 TRACE("()\n");
1319 if (uView == LVS_ICON)
1321 nItemWidth = infoPtr->iconSpacing.cx;
1323 else if (uView == LVS_REPORT)
1325 /* calculate width of header */
1326 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1327 for (i = 0; i < nHeaderItemCount; i++)
1329 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1331 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1335 else
1337 /* get width of string */
1338 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1340 /* default label size */
1341 if (GETITEMCOUNT(infoPtr) == 0)
1343 nItemWidth = DEFAULT_COLUMN_WIDTH;
1345 else
1347 if (nItemWidth == 0)
1349 nItemWidth = DEFAULT_LABEL_WIDTH;
1351 else
1353 /* add padding */
1354 nItemWidth += WIDTH_PADDING;
1356 if (infoPtr->himlSmall != NULL)
1358 nItemWidth += infoPtr->iconSize.cx;
1361 if (infoPtr->himlState != NULL)
1363 nItemWidth += infoPtr->iconSize.cx;
1369 return nItemWidth;
1372 /***
1373 * DESCRIPTION:
1374 * Retrieves and saves important text metrics info for the current
1375 * Listview font.
1377 * PARAMETER(S):
1378 * [I] infoPtr : valid pointer to the listview structure
1381 static VOID LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1383 TEXTMETRICW tm;
1384 HDC hdc = GetDC(infoPtr->hwndSelf);
1385 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1386 INT oldHeight, oldACW;
1388 GetTextMetricsW(hdc, &tm);
1390 oldHeight = infoPtr->ntmHeight;
1391 oldACW = infoPtr->ntmAveCharWidth;
1392 infoPtr->ntmHeight = tm.tmHeight;
1393 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1395 SelectObject(hdc, hOldFont);
1396 ReleaseDC(infoPtr->hwndSelf, hdc);
1397 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1398 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1402 /***
1403 * DESCRIPTION:
1404 * Calculates the height of an item.
1406 * PARAMETER(S):
1407 * [I] infoPtr : valid pointer to the listview structure
1409 * RETURN:
1410 * Returns item height.
1412 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1414 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
1415 INT nItemHeight = 0;
1417 if (uView == LVS_ICON)
1419 nItemHeight = infoPtr->iconSpacing.cy;
1421 else
1423 if(infoPtr->himlState || infoPtr->himlSmall)
1424 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1425 else
1426 nItemHeight = infoPtr->ntmHeight;
1429 return nItemHeight;
1433 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1435 LISTVIEW_SELECTION *selection;
1436 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1437 INT i;
1439 TRACE("Selections are:\n");
1440 for (i = 0; i < topSelection; i++)
1442 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1443 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1447 /***
1448 * DESCRIPTION:
1449 * A compare function for selection ranges
1451 *PARAMETER(S)
1452 * [I] LPVOID : Item 1;
1453 * [I] LPVOID : Item 2;
1454 * [I] LPARAM : flags
1456 *RETURNS:
1457 * >0 : if Item 1 > Item 2
1458 * <0 : if Item 2 > Item 1
1459 * 0 : if Item 1 == Item 2
1461 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1462 LPARAM flags)
1464 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1465 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1466 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1467 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1468 int rc=0;
1470 if (u1 < l2)
1471 rc= -1;
1473 if (u2 < l1)
1474 rc= 1;
1476 return rc;
1480 * DESCRIPTION:
1481 * Adds a selection range.
1483 * PARAMETER(S):
1484 * [I] infoPtr : valid pointer to the listview structure
1485 * [I] INT : lower item index
1486 * [I] INT : upper item index
1488 * RETURN:
1489 * None
1491 static VOID LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lItem, INT uItem)
1493 LISTVIEW_SELECTION *selection;
1494 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1495 BOOL lowerzero = FALSE;
1497 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1498 selection->lower = lItem;
1499 selection->upper = uItem;
1501 TRACE("Add range %i - %i\n", lItem, uItem);
1502 if (topSelection)
1504 LISTVIEW_SELECTION *checkselection,*checkselection2;
1505 INT index,mergeindex;
1507 /* find overlapping selections */
1508 /* we want to catch adjacent ranges so expand our range by 1 */
1510 selection->upper++;
1511 if (selection->lower == 0)
1512 lowerzero = TRUE;
1513 else
1514 selection->lower--;
1516 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1517 LISTVIEW_CompareSelectionRanges,
1518 0,0);
1519 selection->upper --;
1520 if (lowerzero)
1521 lowerzero = FALSE;
1522 else
1523 selection->lower ++;
1525 if (index >=0)
1527 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1528 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1529 checkselection->upper);
1531 checkselection->lower = min(selection->lower,checkselection->lower);
1532 checkselection->upper = max(selection->upper,checkselection->upper);
1534 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1535 checkselection->upper);
1537 COMCTL32_Free(selection);
1539 /* merge now common selection ranges in the lower group*/
1542 checkselection->upper ++;
1543 if (checkselection->lower == 0)
1544 lowerzero = TRUE;
1545 else
1546 checkselection->lower --;
1548 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1549 checkselection->upper);
1551 /* not sorted yet */
1552 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1553 LISTVIEW_CompareSelectionRanges, 0,
1556 checkselection->upper --;
1557 if (lowerzero)
1558 lowerzero = FALSE;
1559 else
1560 checkselection->lower ++;
1562 if (mergeindex >=0 && mergeindex != index)
1564 TRACE("Merge with index %i\n",mergeindex);
1565 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1566 mergeindex);
1567 checkselection->lower = min(checkselection->lower,
1568 checkselection2->lower);
1569 checkselection->upper = max(checkselection->upper,
1570 checkselection2->upper);
1571 COMCTL32_Free(checkselection2);
1572 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1573 index --;
1576 while (mergeindex > -1 && mergeindex <index);
1578 /* merge now common selection ranges in the upper group*/
1581 checkselection->upper ++;
1582 if (checkselection->lower == 0)
1583 lowerzero = TRUE;
1584 else
1585 checkselection->lower --;
1587 TRACE("search upper range %i (%lu - %lu)\n",index,
1588 checkselection->lower, checkselection->upper);
1590 /* not sorted yet */
1591 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1592 index+1,
1593 LISTVIEW_CompareSelectionRanges, 0,
1596 checkselection->upper --;
1597 if (lowerzero)
1598 lowerzero = FALSE;
1599 else
1600 checkselection->lower ++;
1602 if (mergeindex >=0 && mergeindex !=index)
1604 TRACE("Merge with index %i\n",mergeindex);
1605 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1606 mergeindex);
1607 checkselection->lower = min(checkselection->lower,
1608 checkselection2->lower);
1609 checkselection->upper = max(checkselection->upper,
1610 checkselection2->upper);
1611 COMCTL32_Free(checkselection2);
1612 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1615 while (mergeindex > -1);
1617 else
1620 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1621 LISTVIEW_CompareSelectionRanges, 0,
1622 DPAS_INSERTAFTER);
1624 TRACE("Insert before index %i\n",index);
1625 if (index == -1)
1626 index = 0;
1627 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1630 else
1632 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1635 * Incase of error
1637 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1638 LISTVIEW_PrintSelectionRanges(infoPtr);
1642 * DESCRIPTION:
1643 * check if a specified index is selected.
1645 * PARAMETER(S):
1646 * [I] infoPtr : valid pointer to the listview structure
1647 * [I] INT : item index
1649 * RETURN:
1650 * None
1652 static BOOL LISTVIEW_IsSelected(LISTVIEW_INFO *infoPtr, INT nItem)
1654 LISTVIEW_SELECTION selection;
1655 INT index;
1657 selection.upper = nItem;
1658 selection.lower = nItem;
1660 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1661 LISTVIEW_CompareSelectionRanges,
1662 0,DPAS_SORTED);
1663 if (index != -1)
1664 return TRUE;
1665 else
1666 return FALSE;
1669 /***
1670 * DESCRIPTION:
1671 * Removes all selection ranges
1673 * Parameters(s):
1674 * [I] infoPtr : valid pointer to the listview structure
1676 * RETURNS:
1677 * SUCCESS : TRUE
1678 * FAILURE : TRUE
1680 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
1682 LISTVIEW_SELECTION *selection;
1683 INT i;
1684 LVITEMW item;
1686 TRACE("()\n");
1688 ZeroMemory(&item,sizeof(item));
1689 item.stateMask = LVIS_SELECTED;
1693 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1694 if (selection)
1696 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1697 for (i = selection->lower; i<=selection->upper; i++)
1698 LISTVIEW_SetItemState(infoPtr,i,&item);
1699 LISTVIEW_RemoveSelectionRange(infoPtr,selection->lower,selection->upper);
1702 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1704 TRACE("done\n");
1705 return TRUE;
1709 * DESCRIPTION:
1710 * Removes a range selections.
1712 * PARAMETER(S):
1713 * [I] infoPtr : valid pointer to the listview structure
1714 * [I] INT : lower item index
1715 * [I] INT : upper item index
1717 * RETURN:
1718 * None
1720 static VOID LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lItem, INT uItem)
1722 LISTVIEW_SELECTION removeselection,*checkselection;
1723 INT index;
1725 removeselection.lower = lItem;
1726 removeselection.upper = uItem;
1728 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1729 LISTVIEW_PrintSelectionRanges(infoPtr);
1731 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1732 LISTVIEW_CompareSelectionRanges,
1733 0,0);
1735 if (index == -1)
1736 return;
1739 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1740 index);
1742 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1743 checkselection->upper);
1745 /* case 1: Same */
1746 if ((checkselection->upper == removeselection.upper) &&
1747 (checkselection->lower == removeselection.lower))
1749 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1750 TRACE("Case 1\n");
1752 /* case 2: engulf */
1753 else if (((checkselection->upper < removeselection.upper) &&
1754 (checkselection->lower > removeselection.lower))||
1755 ((checkselection->upper <= removeselection.upper) &&
1756 (checkselection->lower > removeselection.lower)) ||
1757 ((checkselection->upper < removeselection.upper) &&
1758 (checkselection->lower >= removeselection.lower)))
1761 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1762 /* do it again because others may also get caught */
1763 TRACE("Case 2\n");
1764 LISTVIEW_RemoveSelectionRange(infoPtr,lItem,uItem);
1766 /* case 3: overlap upper */
1767 else if ((checkselection->upper < removeselection.upper) &&
1768 (checkselection->lower < removeselection.lower))
1770 checkselection->upper = removeselection.lower - 1;
1771 TRACE("Case 3\n");
1772 LISTVIEW_RemoveSelectionRange(infoPtr,lItem,uItem);
1774 /* case 4: overlap lower */
1775 else if ((checkselection->upper > removeselection.upper) &&
1776 (checkselection->lower > removeselection.lower))
1778 checkselection->lower = removeselection.upper + 1;
1779 TRACE("Case 4\n");
1780 LISTVIEW_RemoveSelectionRange(infoPtr,lItem,uItem);
1782 /* case 5: fully internal */
1783 else if (checkselection->upper == removeselection.upper)
1784 checkselection->upper = removeselection.lower - 1;
1785 else if (checkselection->lower == removeselection.lower)
1786 checkselection->lower = removeselection.upper + 1;
1787 else
1789 /* bisect the range */
1790 LISTVIEW_SELECTION *newselection;
1792 newselection = (LISTVIEW_SELECTION *)
1793 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1794 newselection -> lower = checkselection->lower;
1795 newselection -> upper = removeselection.lower - 1;
1796 checkselection -> lower = removeselection.upper + 1;
1797 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1798 TRACE("Case 5\n");
1799 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1801 LISTVIEW_PrintSelectionRanges(infoPtr);
1805 * DESCRIPTION:
1806 * Updates the various indices after an item has been inserted or deleted.
1808 * PARAMETER(S):
1809 * [I] infoPtr : valid pointer to the listview structure
1810 * [I] INT : item index
1811 * [I] INT : Direction of shift, +1 or -1.
1813 * RETURN:
1814 * None
1816 static VOID LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
1818 LISTVIEW_SELECTION selection,*checkselection;
1819 INT index;
1821 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1823 selection.upper = nItem;
1824 selection.lower = nItem;
1826 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1827 LISTVIEW_CompareSelectionRanges,
1828 0,DPAS_SORTED|DPAS_INSERTAFTER);
1830 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1832 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1833 if ((checkselection->lower >= nItem)&&
1834 ((int)(checkselection->lower + direction) >= 0))
1835 checkselection->lower += direction;
1836 if ((checkselection->upper >= nItem)&&
1837 ((int)(checkselection->upper + direction) >= 0))
1838 checkselection->upper += direction;
1839 index ++;
1842 /* Note that the following will fail if direction != +1 and -1 */
1843 if (infoPtr->nSelectionMark > nItem)
1844 infoPtr->nSelectionMark += direction;
1845 else if (infoPtr->nSelectionMark == nItem)
1847 if (direction > 0)
1848 infoPtr->nSelectionMark += direction;
1849 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1850 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1853 if (infoPtr->nFocusedItem > nItem)
1854 infoPtr->nFocusedItem += direction;
1855 else if (infoPtr->nFocusedItem == nItem)
1857 if (direction > 0)
1858 infoPtr->nFocusedItem += direction;
1859 else
1861 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1862 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1863 if (infoPtr->nFocusedItem >= 0)
1864 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
1867 /* But we are not supposed to modify nHotItem! */
1872 * DESCRIPTION:
1873 * Adds a block of selections.
1875 * PARAMETER(S):
1876 * [I] infoPtr : valid pointer to the listview structure
1877 * [I] INT : item index
1879 * RETURN:
1880 * None
1882 static VOID LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
1884 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1885 INT nLast = max(infoPtr->nSelectionMark, nItem);
1886 INT i;
1887 LVITEMW item;
1889 if (nFirst == -1)
1890 nFirst = nItem;
1892 ZeroMemory(&item,sizeof(item));
1893 item.stateMask = LVIS_SELECTED;
1894 item.state = LVIS_SELECTED;
1896 for (i = nFirst; i <= nLast; i++)
1897 LISTVIEW_SetItemState(infoPtr,i,&item);
1899 LISTVIEW_SetItemFocus(infoPtr, nItem);
1900 infoPtr->nSelectionMark = nItem;
1904 /***
1905 * DESCRIPTION:
1906 * Adds a single selection.
1908 * PARAMETER(S):
1909 * [I] infoPtr : valid pointer to the listview structure
1910 * [I] INT : item index
1912 * RETURN:
1913 * None
1915 static VOID LISTVIEW_AddSelection(LISTVIEW_INFO *infoPtr, INT nItem)
1917 LVITEMW item;
1919 ZeroMemory(&item,sizeof(item));
1920 item.state = LVIS_SELECTED;
1921 item.stateMask = LVIS_SELECTED;
1923 LISTVIEW_SetItemState(infoPtr,nItem,&item);
1925 LISTVIEW_SetItemFocus(infoPtr, nItem);
1926 infoPtr->nSelectionMark = nItem;
1929 /***
1930 * DESCRIPTION:
1931 * Selects or unselects an item.
1933 * PARAMETER(S):
1934 * [I] infoPtr : valid pointer to the listview structure
1935 * [I] INT : item index
1937 * RETURN:
1938 * SELECT: TRUE
1939 * UNSELECT : FALSE
1941 static BOOL LISTVIEW_ToggleSelection(LISTVIEW_INFO *infoPtr, INT nItem)
1943 BOOL bResult;
1944 LVITEMW item;
1946 ZeroMemory(&item,sizeof(item));
1947 item.stateMask = LVIS_SELECTED;
1949 if (LISTVIEW_IsSelected(infoPtr,nItem))
1951 LISTVIEW_SetItemState(infoPtr,nItem,&item);
1952 bResult = FALSE;
1954 else
1956 item.state = LVIS_SELECTED;
1957 LISTVIEW_SetItemState(infoPtr,nItem,&item);
1958 bResult = TRUE;
1961 LISTVIEW_SetItemFocus(infoPtr, nItem);
1962 infoPtr->nSelectionMark = nItem;
1964 return bResult;
1967 /***
1968 * DESCRIPTION:
1969 * Selects items based on view coordinates.
1971 * PARAMETER(S):
1972 * [I] infoPtr : valid pointer to the listview structure
1973 * [I] RECT : selection rectangle
1975 * RETURN:
1976 * None
1978 static VOID LISTVIEW_SetSelectionRect(LISTVIEW_INFO *infoPtr, RECT rcSelRect)
1980 POINT ptItem;
1981 INT i;
1982 LVITEMW item;
1984 ZeroMemory(&item,sizeof(item));
1985 item.stateMask = LVIS_SELECTED;
1987 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1989 LISTVIEW_GetItemPosition(infoPtr, i, &ptItem);
1991 if (PtInRect(&rcSelRect, ptItem))
1992 item.state = LVIS_SELECTED;
1993 else
1994 item.state = 0;
1995 LISTVIEW_SetItemState(infoPtr,i,&item);
1999 /***
2000 * DESCRIPTION:
2001 * Sets a single group selection.
2003 * PARAMETER(S):
2004 * [I] infoPtr : valid pointer to the listview structure
2005 * [I] INT : item index
2007 * RETURN:
2008 * None
2010 static VOID LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2012 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
2013 LVITEMW item;
2015 ZeroMemory(&item,sizeof(item));
2016 item.stateMask = LVIS_SELECTED;
2018 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2020 INT i;
2021 INT nFirst, nLast;
2023 if (infoPtr->nSelectionMark == -1)
2025 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2027 else
2029 nFirst = min(infoPtr->nSelectionMark, nItem);
2030 nLast = max(infoPtr->nSelectionMark, nItem);
2033 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2035 if ((i < nFirst) || (i > nLast))
2036 item.state = 0;
2037 else
2038 item.state = LVIS_SELECTED;
2039 LISTVIEW_SetItemState(infoPtr,i,&item);
2042 else
2044 RECT rcItem;
2045 RECT rcSelMark;
2046 RECT rcSel;
2047 LISTVIEW_GetItemBoundBox(infoPtr, nItem, &rcItem);
2048 LISTVIEW_GetItemBoundBox(infoPtr, infoPtr->nSelectionMark, &rcSelMark);
2049 rcSel.left = min(rcSelMark.left, rcItem.left);
2050 rcSel.top = min(rcSelMark.top, rcItem.top);
2051 rcSel.right = max(rcSelMark.right, rcItem.right);
2052 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2053 LISTVIEW_SetSelectionRect(infoPtr, rcSel);
2054 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2055 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2056 infoPtr->nSelectionMark,
2057 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2058 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2062 LISTVIEW_SetItemFocus(infoPtr, nItem);
2065 /***
2066 * DESCRIPTION:
2067 * Manages the item focus.
2069 * PARAMETER(S):
2070 * [I] infoPtr : valid pointer to the listview structure
2071 * [I] INT : item index
2073 * RETURN:
2074 * TRUE : focused item changed
2075 * FALSE : focused item has NOT changed
2077 static BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2079 BOOL bResult = FALSE;
2080 LVITEMW lvItem;
2082 if (infoPtr->nFocusedItem != nItem)
2084 if (infoPtr->nFocusedItem >= 0)
2086 INT oldFocus = infoPtr->nFocusedItem;
2087 bResult = TRUE;
2088 infoPtr->nFocusedItem = -1;
2089 ZeroMemory(&lvItem, sizeof(lvItem));
2090 lvItem.stateMask = LVIS_FOCUSED;
2091 LISTVIEW_SetItemState(infoPtr, oldFocus, &lvItem);
2095 lvItem.state = LVIS_FOCUSED;
2096 lvItem.stateMask = LVIS_FOCUSED;
2097 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2099 infoPtr->nFocusedItem = nItem;
2100 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
2103 return bResult;
2106 /***
2107 * DESCRIPTION:
2108 * Sets a single selection.
2110 * PARAMETER(S):
2111 * [I] infoPtr : valid pointer to the listview structure
2112 * [I] INT : item index
2114 * RETURN:
2115 * None
2117 static VOID LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2119 LVITEMW lvItem;
2121 ZeroMemory(&lvItem, sizeof(lvItem));
2122 lvItem.stateMask = LVIS_FOCUSED;
2123 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &lvItem);
2125 LISTVIEW_RemoveAllSelections(infoPtr);
2127 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2128 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2129 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2131 infoPtr->nFocusedItem = nItem;
2132 infoPtr->nSelectionMark = nItem;
2135 /***
2136 * DESCRIPTION:
2137 * Set selection(s) with keyboard.
2139 * PARAMETER(S):
2140 * [I] infoPtr : valid pointer to the listview structure
2141 * [I] INT : item index
2143 * RETURN:
2144 * SUCCESS : TRUE (needs to be repainted)
2145 * FAILURE : FALSE (nothing has changed)
2147 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2149 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2150 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2151 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2152 BOOL bResult = FALSE;
2154 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2156 if (lStyle & LVS_SINGLESEL)
2158 bResult = TRUE;
2159 LISTVIEW_SetSelection(infoPtr, nItem);
2160 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2162 else
2164 if (wShift)
2166 bResult = TRUE;
2167 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2169 else if (wCtrl)
2171 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2173 else
2175 bResult = TRUE;
2176 LISTVIEW_SetSelection(infoPtr, nItem);
2177 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2182 return bResult;
2185 /***
2186 * DESCRIPTION:
2187 * Called when the mouse is being actively tracked and has hovered for a specified
2188 * amount of time
2190 * PARAMETER(S):
2191 * [I] infoPtr : valid pointer to the listview structure
2192 * [I] fwKeys : key indicator
2193 * [I] pts : mouse position
2195 * RETURN:
2196 * 0 if the message was processed, non-zero if there was an error
2198 * INFO:
2199 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2200 * over the item for a certain period of time.
2203 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2205 POINT pt = { pts.x, pts.y };
2207 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2208 /* FIXME: select the item under the cursor */
2209 LISTVIEW_MouseSelection(infoPtr, pt);
2212 return 0;
2215 /***
2216 * DESCRIPTION:
2217 * Called whenever WM_MOUSEMOVE is received.
2219 * PARAMETER(S):
2220 * [I] infoPtr : valid pointer to the listview structure
2221 * [I] fwKeys : key indicator
2222 * [I] pts : mouse position
2224 * RETURN:
2225 * 0 if the message is processed, non-zero if there was an error
2227 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2229 TRACKMOUSEEVENT trackinfo;
2231 /* see if we are supposed to be tracking mouse hovering */
2232 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2233 /* fill in the trackinfo struct */
2234 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2235 trackinfo.dwFlags = TME_QUERY;
2236 trackinfo.hwndTrack = infoPtr->hwndSelf;
2237 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2239 /* see if we are already tracking this hwnd */
2240 _TrackMouseEvent(&trackinfo);
2242 if(!(trackinfo.dwFlags & TME_HOVER)) {
2243 trackinfo.dwFlags = TME_HOVER;
2245 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2246 _TrackMouseEvent(&trackinfo);
2250 return 0;
2253 /***
2254 * DESCRIPTION:
2255 * Selects an item based on coordinates.
2257 * PARAMETER(S):
2258 * [I] infoPtr : valid pointer to the listview structure
2259 * [I] pt : mouse click ccordinates
2261 * RETURN:
2262 * SUCCESS : item index
2263 * FAILURE : -1
2265 static INT LISTVIEW_MouseSelection(LISTVIEW_INFO *infoPtr, POINT pt)
2267 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2268 INT i, topindex, bottomindex;
2269 RECT rcItem;
2271 topindex = LISTVIEW_GetTopIndex(infoPtr);
2272 if ((lStyle & LVS_TYPEMASK) == LVS_REPORT) {
2273 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
2274 bottomindex = min(bottomindex, GETITEMCOUNT(infoPtr));
2275 } else {
2276 bottomindex = GETITEMCOUNT(infoPtr);
2279 for (i = topindex; i < bottomindex; i++)
2281 rcItem.left = LVIR_SELECTBOUNDS;
2282 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
2284 if (PtInRect(&rcItem, pt)) return i;
2288 return -1;
2291 /***
2292 * DESCRIPTION:
2293 * Removes a column.
2295 * PARAMETER(S):
2296 * [IO] HDPA : dynamic pointer array handle
2297 * [I] INT : column index (subitem index)
2299 * RETURN:
2300 * SUCCCESS : TRUE
2301 * FAILURE : FALSE
2303 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2305 BOOL bResult = TRUE;
2306 HDPA hdpaSubItems;
2307 INT i;
2309 for (i = 0; i < hdpaItems->nItemCount; i++)
2311 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2312 if (hdpaSubItems != NULL)
2314 if (!LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem))
2316 bResult = FALSE;
2321 return bResult;
2324 /***
2325 * DESCRIPTION:
2326 * Removes a subitem at a given position.
2328 * PARAMETER(S):
2329 * [IO] HDPA : dynamic pointer array handle
2330 * [I] INT : subitem index
2332 * RETURN:
2333 * SUCCCESS : TRUE
2334 * FAILURE : FALSE
2336 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2338 LISTVIEW_SUBITEM *lpSubItem;
2339 INT i;
2341 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2343 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2344 if (lpSubItem != NULL)
2346 if (lpSubItem->iSubItem == nSubItem)
2348 /* free string */
2349 if (is_textW(lpSubItem->pszText))
2350 COMCTL32_Free(lpSubItem->pszText);
2352 /* free item */
2353 COMCTL32_Free(lpSubItem);
2355 /* free dpa memory */
2356 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2357 return FALSE;
2359 else if (lpSubItem->iSubItem > nSubItem)
2360 return TRUE;
2364 return TRUE;
2367 /***
2368 * DESCRIPTION:
2369 * Compares the item information.
2371 * PARAMETER(S):
2372 * [I] LISTVIEW_ITEM *: destination item
2373 * [I] LPLVITEM : source item
2374 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2376 * RETURN:
2377 * SUCCCESS : TRUE (EQUAL)
2378 * FAILURE : FALSE (NOT EQUAL)
2380 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2382 UINT uChanged = 0;
2384 if ((lpItem != NULL) && (lpLVItem != NULL))
2386 if (lpLVItem->mask & LVIF_STATE)
2388 if ((lpItem->state & lpLVItem->stateMask) !=
2389 (lpLVItem->state & lpLVItem->stateMask))
2390 uChanged |= LVIF_STATE;
2393 if (lpLVItem->mask & LVIF_IMAGE)
2395 if (lpItem->iImage != lpLVItem->iImage)
2396 uChanged |= LVIF_IMAGE;
2399 if (lpLVItem->mask & LVIF_PARAM)
2401 if (lpItem->lParam != lpLVItem->lParam)
2402 uChanged |= LVIF_PARAM;
2405 if (lpLVItem->mask & LVIF_INDENT)
2407 if (lpItem->iIndent != lpLVItem->iIndent)
2408 uChanged |= LVIF_INDENT;
2411 if (lpLVItem->mask & LVIF_TEXT)
2413 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2415 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2416 uChanged |= LVIF_TEXT;
2418 else
2420 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2422 uChanged |= LVIF_TEXT;
2424 else
2426 if (lpLVItem->pszText)
2428 if (lpItem->pszText)
2430 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2431 if (pszText && strcmpW(pszText, lpItem->pszText))
2432 uChanged |= LVIF_TEXT;
2433 textfreeT(pszText, isW);
2435 else
2437 uChanged |= LVIF_TEXT;
2440 else
2442 if (lpItem->pszText)
2443 uChanged |= LVIF_TEXT;
2449 return uChanged;
2452 /***
2453 * DESCRIPTION:
2454 * Initializes item attributes.
2456 * PARAMETER(S):
2457 * [I] infoPtr : valid pointer to the listview structure
2458 * [O] LISTVIEW_ITEM *: destination item
2459 * [I] LPLVITEM : source item
2460 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2462 * RETURN:
2463 * SUCCCESS : TRUE
2464 * FAILURE : FALSE
2466 static BOOL LISTVIEW_InitItemT(LISTVIEW_INFO *infoPtr, LISTVIEW_ITEM *lpItem,
2467 LPLVITEMW lpLVItem, BOOL isW)
2469 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2470 BOOL bResult = FALSE;
2472 if ((lpItem != NULL) && (lpLVItem != NULL))
2474 bResult = TRUE;
2476 if (lpLVItem->mask & LVIF_STATE)
2478 lpItem->state &= ~lpLVItem->stateMask;
2479 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2482 if (lpLVItem->mask & LVIF_IMAGE)
2483 lpItem->iImage = lpLVItem->iImage;
2485 if (lpLVItem->mask & LVIF_PARAM)
2486 lpItem->lParam = lpLVItem->lParam;
2488 if (lpLVItem->mask & LVIF_INDENT)
2489 lpItem->iIndent = lpLVItem->iIndent;
2491 if (lpLVItem->mask & LVIF_TEXT)
2493 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2495 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2496 return FALSE;
2498 if (is_textW(lpItem->pszText))
2499 COMCTL32_Free(lpItem->pszText);
2501 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2503 else
2504 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2508 return bResult;
2511 /***
2512 * DESCRIPTION:
2513 * Initializes subitem attributes.
2515 * NOTE: The documentation specifies that the operation fails if the user
2516 * tries to set the indent of a subitem.
2518 * PARAMETER(S):
2519 * [I] infoPtr : valid pointer to the listview structure
2520 * [O] LISTVIEW_SUBITEM *: destination subitem
2521 * [I] LPLVITEM : source subitem
2522 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2524 * RETURN:
2525 * SUCCCESS : TRUE
2526 * FAILURE : FALSE
2528 static BOOL LISTVIEW_InitSubItemT(LISTVIEW_INFO *infoPtr, LISTVIEW_SUBITEM *lpSubItem,
2529 LPLVITEMW lpLVItem, BOOL isW)
2531 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2532 BOOL bResult = FALSE;
2534 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2536 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2538 if (!(lpLVItem->mask & LVIF_INDENT))
2540 bResult = TRUE;
2542 lpSubItem->iSubItem = lpLVItem->iSubItem;
2544 if (lpLVItem->mask & LVIF_IMAGE)
2545 lpSubItem->iImage = lpLVItem->iImage;
2547 if (lpLVItem->mask & LVIF_TEXT)
2549 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2551 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2552 return FALSE;
2554 if (is_textW(lpSubItem->pszText))
2555 COMCTL32_Free(lpSubItem->pszText);
2557 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2559 else
2560 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2565 return bResult;
2568 /***
2569 * DESCRIPTION:
2570 * Adds a subitem at a given position (column index).
2572 * PARAMETER(S):
2573 * [I] infoPtr : valid pointer to the listview structure
2574 * [I] LPLVITEM : new subitem atttributes
2575 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2577 * RETURN:
2578 * SUCCESS : TRUE
2579 * FAILURE : FALSE
2581 static BOOL LISTVIEW_AddSubItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2583 LISTVIEW_SUBITEM *lpSubItem = NULL;
2584 BOOL bResult = FALSE;
2585 HDPA hdpaSubItems;
2586 INT nPosition, nItem;
2587 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2589 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2591 if (lStyle & LVS_OWNERDATA)
2592 return FALSE;
2594 if (lpLVItem != NULL)
2596 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2597 if (hdpaSubItems != NULL)
2599 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2600 if (lpSubItem != NULL)
2602 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2603 if (LISTVIEW_InitSubItemT(infoPtr, lpSubItem, lpLVItem, isW))
2605 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2606 lpSubItem->iSubItem);
2607 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2608 if (nItem != -1) bResult = TRUE;
2614 /* cleanup if unsuccessful */
2615 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2617 return bResult;
2620 /***
2621 * DESCRIPTION:
2622 * Finds the dpa insert position (array index).
2624 * PARAMETER(S):
2625 * [I] infoPtr : valid pointer to the listview structure
2626 * [I] INT : subitem index
2628 * RETURN:
2629 * SUCCESS : TRUE
2630 * FAILURE : FALSE
2632 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2634 LISTVIEW_SUBITEM *lpSubItem;
2635 INT i;
2637 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2639 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2640 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2641 return i;
2644 return hdpaSubItems->nItemCount;
2647 /***
2648 * DESCRIPTION:
2649 * Retrieves a listview subitem at a given position (column index).
2651 * PARAMETER(S):
2652 * [I] infoPtr : valid pointer to the listview structure
2653 * [I] INT : subitem index
2655 * RETURN:
2656 * SUCCESS : TRUE
2657 * FAILURE : FALSE
2659 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2661 LISTVIEW_SUBITEM *lpSubItem;
2662 INT i;
2664 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2666 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2667 if (lpSubItem != NULL)
2669 if (lpSubItem->iSubItem == nSubItem)
2670 return lpSubItem;
2671 else if (lpSubItem->iSubItem > nSubItem)
2672 return NULL;
2676 return NULL;
2679 /***
2680 * DESCRIPTION:
2681 * Sets item attributes.
2683 * PARAMETER(S):
2684 * [I] infoPtr : valid pointer to the listview structure
2685 * [I] LPLVITEM : new item atttributes
2686 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2688 * RETURN:
2689 * SUCCESS : TRUE
2690 * FAILURE : FALSE
2692 static BOOL LISTVIEW_SetMainItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2694 BOOL bResult = FALSE;
2695 HDPA hdpaSubItems;
2696 LISTVIEW_ITEM *lpItem;
2697 NMLISTVIEW nmlv;
2698 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2699 UINT uChanged;
2700 UINT uView = lStyle & LVS_TYPEMASK;
2701 INT item_width;
2702 RECT rcItem;
2704 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2706 if (lStyle & LVS_OWNERDATA)
2708 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2710 LVITEMW itm;
2712 ZeroMemory(&itm, sizeof(itm));
2713 itm.mask = LVIF_STATE | LVIF_PARAM;
2714 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2715 itm.iItem = lpLVItem->iItem;
2716 itm.iSubItem = 0;
2717 ListView_GetItemW(infoPtr->hwndSelf, &itm);
2720 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2721 nmlv.uNewState = lpLVItem->state;
2722 nmlv.uOldState = itm.state;
2723 nmlv.uChanged = LVIF_STATE;
2724 nmlv.lParam = itm.lParam;
2725 nmlv.iItem = lpLVItem->iItem;
2727 if ((itm.state & lpLVItem->stateMask) !=
2728 (lpLVItem->state & lpLVItem->stateMask))
2731 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent
2732 * by LVS_OWERNDATA list controls
2734 if (lpLVItem->stateMask & LVIS_FOCUSED)
2736 if (lpLVItem->state & LVIS_FOCUSED)
2737 infoPtr->nFocusedItem = lpLVItem->iItem;
2738 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2739 infoPtr->nFocusedItem = -1;
2741 if (lpLVItem->stateMask & LVIS_SELECTED)
2743 if (lpLVItem->state & LVIS_SELECTED)
2745 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2746 LISTVIEW_AddSelectionRange(infoPtr,lpLVItem->iItem,lpLVItem->iItem);
2748 else
2749 LISTVIEW_RemoveSelectionRange(infoPtr,lpLVItem->iItem,
2750 lpLVItem->iItem);
2753 listview_notify(infoPtr, LVN_ITEMCHANGED, &nmlv);
2755 rcItem.left = LVIR_BOUNDS;
2756 LISTVIEW_GetItemRect(infoPtr, lpLVItem->iItem, &rcItem);
2757 if (!infoPtr->bIsDrawing)
2758 InvalidateRect(infoPtr->hwndSelf, &rcItem, TRUE);
2760 return TRUE;
2762 return FALSE;
2765 if (lpLVItem != NULL)
2767 if (lpLVItem->iSubItem == 0)
2769 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2770 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2772 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2773 if (lpItem != NULL)
2775 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2776 nmlv.lParam = lpItem->lParam;
2777 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2778 if (uChanged != 0)
2780 if (uChanged & LVIF_STATE)
2782 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2783 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2785 if (nmlv.uNewState & LVIS_SELECTED)
2788 * This is redundant if called through SetSelection
2790 * however is required if the used directly calls SetItem
2791 * to set the selection.
2793 if (lStyle & LVS_SINGLESEL)
2794 LISTVIEW_RemoveAllSelections(infoPtr);
2796 LISTVIEW_AddSelectionRange(infoPtr,lpLVItem->iItem,
2797 lpLVItem->iItem);
2799 else if (lpLVItem->stateMask & LVIS_SELECTED)
2801 LISTVIEW_RemoveSelectionRange(infoPtr,lpLVItem->iItem,
2802 lpLVItem->iItem);
2804 if (nmlv.uNewState & LVIS_FOCUSED)
2807 * This is a fun hoop to jump to try to catch if
2808 * the user is calling us directly to call focus or if
2809 * this function is being called as a result of a
2810 * SetItemFocus call.
2812 if (infoPtr->nFocusedItem >= 0)
2813 LISTVIEW_SetItemFocus(infoPtr, lpLVItem->iItem);
2817 nmlv.uChanged = uChanged;
2818 nmlv.iItem = lpLVItem->iItem;
2819 nmlv.lParam = lpItem->lParam;
2820 /* send LVN_ITEMCHANGING notification */
2821 listview_notify(infoPtr, LVN_ITEMCHANGING, &nmlv);
2823 /* copy information */
2824 bResult = LISTVIEW_InitItemT(infoPtr, lpItem, lpLVItem, isW);
2826 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2827 based on the width of the items text */
2828 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2830 item_width = LISTVIEW_GetStringWidthT(infoPtr, lpItem->pszText, TRUE);
2832 if(item_width > infoPtr->nItemWidth)
2833 infoPtr->nItemWidth = item_width;
2836 /* send LVN_ITEMCHANGED notification */
2837 listview_notify(infoPtr, LVN_ITEMCHANGED, &nmlv);
2839 else
2841 bResult = TRUE;
2844 if (uChanged)
2846 rcItem.left = LVIR_BOUNDS;
2847 LISTVIEW_GetItemRect(infoPtr, lpLVItem->iItem, &rcItem);
2848 if (!infoPtr->bIsDrawing)
2849 InvalidateRect(infoPtr->hwndSelf, &rcItem, TRUE);
2856 return bResult;
2859 /***
2860 * DESCRIPTION:
2861 * Sets subitem attributes.
2863 * PARAMETER(S):
2864 * [I] infoPtr : valid pointer to the listview structure
2865 * [I] LPLVITEM : new subitem atttributes
2866 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2868 * RETURN:
2869 * SUCCESS : TRUE
2870 * FAILURE : FALSE
2872 static BOOL LISTVIEW_SetSubItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2874 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2875 BOOL bResult = FALSE;
2876 HDPA hdpaSubItems;
2877 LISTVIEW_SUBITEM *lpSubItem;
2878 RECT rcItem;
2880 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2882 if (lStyle & LVS_OWNERDATA)
2883 return FALSE;
2885 if (lpLVItem != NULL)
2887 if (lpLVItem->iSubItem > 0)
2889 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2890 if (hdpaSubItems != NULL)
2892 /* set subitem only if column is present */
2893 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2895 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2896 if (lpSubItem != NULL)
2897 bResult = LISTVIEW_InitSubItemT(infoPtr, lpSubItem, lpLVItem, isW);
2898 else
2899 bResult = LISTVIEW_AddSubItemT(infoPtr, lpLVItem, isW);
2901 rcItem.left = LVIR_BOUNDS;
2902 LISTVIEW_GetItemRect(infoPtr, lpLVItem->iItem, &rcItem);
2903 InvalidateRect(infoPtr->hwndSelf, &rcItem, FALSE);
2909 return bResult;
2912 /***
2913 * DESCRIPTION:
2914 * Sets item attributes.
2916 * PARAMETER(S):
2917 * [I] infoPtr : valid pointer to the listview structure
2918 * [I] LPLVITEM : new item atttributes
2919 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2921 * RETURN:
2922 * SUCCESS : TRUE
2923 * FAILURE : FALSE
2925 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2928 if (!lpLVItem || lpLVItem->iItem < 0 ||
2929 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2930 return FALSE;
2931 if (lpLVItem->iSubItem == 0)
2932 return LISTVIEW_SetMainItemT(infoPtr, lpLVItem, isW);
2933 else
2934 return LISTVIEW_SetSubItemT(infoPtr, lpLVItem, isW);
2937 /***
2938 * DESCRIPTION:
2939 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2941 * PARAMETER(S):
2942 * [I] infoPtr : valid pointer to the listview structure
2944 * RETURN:
2945 * item index
2947 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2949 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
2950 UINT uView = lStyle & LVS_TYPEMASK;
2951 INT nItem = 0;
2952 SCROLLINFO scrollInfo;
2954 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2955 scrollInfo.cbSize = sizeof(SCROLLINFO);
2956 scrollInfo.fMask = SIF_POS;
2958 if (uView == LVS_LIST)
2960 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2961 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2963 else if (uView == LVS_REPORT)
2965 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2966 nItem = scrollInfo.nPos;
2969 return nItem;
2972 /***
2973 * DESCRIPTION:
2974 * Draws a subitem.
2976 * PARAMETER(S):
2977 * [I] infoPtr : valid pointer to the listview structure
2978 * [I] HDC : device context handle
2979 * [I] INT : item index
2980 * [I] INT : subitem index
2981 * [I] RECT * : clipping rectangle
2983 * RETURN:
2984 * None
2986 static VOID LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem,
2987 RECT rcItem, BOOL Selected)
2989 WCHAR szDispText[DISP_TEXT_SIZE];
2990 LVITEMW lvItem;
2991 LVCOLUMNW lvColumn;
2992 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2993 RECT rcTemp;
2994 INT textLeft;
2995 INT nLabelWidth = 0;
2997 TRACE("(hdc=%x, nItem=%d, nSubItem=%d)\n", hdc,
2998 nItem, nSubItem);
3000 /* get information needed for drawing the item */
3001 ZeroMemory(&lvItem, sizeof(lvItem));
3002 lvItem.mask = LVIF_TEXT;
3003 lvItem.iItem = nItem;
3004 lvItem.iSubItem = nSubItem;
3005 lvItem.cchTextMax = DISP_TEXT_SIZE;
3006 lvItem.pszText = szDispText;
3007 *lvItem.pszText = '\0';
3008 LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE);
3009 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3011 ZeroMemory(&lvColumn, sizeof(lvColumn));
3012 lvColumn.mask = LVCF_FMT;
3013 LISTVIEW_GetColumnT(infoPtr, nSubItem, &lvColumn, TRUE);
3014 textLeft = rcItem.left;
3015 TRACE("lvColumn.fmt=%d\n", lvColumn.fmt);
3016 if (lvColumn.fmt != LVCFMT_LEFT)
3018 if ((nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE)))
3020 if (lvColumn.fmt == LVCFMT_RIGHT)
3021 textLeft = rcItem.right - nLabelWidth;
3022 else
3023 textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
3028 /* redraw the background of the item */
3029 rcTemp = rcItem;
3030 if(infoPtr->nColumnCount == (nSubItem + 1))
3031 rcTemp.right = infoPtr->rcList.right;
3032 else
3033 rcTemp.right += WIDTH_PADDING;
3035 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3037 /* set item colors */
3038 if (ListView_GetItemState(infoPtr->hwndSelf,nItem,LVIS_SELECTED) && Selected)
3040 if (infoPtr->bFocus)
3042 SetBkColor(hdc, comctl32_color.clrHighlight);
3043 SetTextColor(hdc, comctl32_color.clrHighlightText);
3045 else
3047 SetBkColor(hdc, comctl32_color.clr3dFace);
3048 SetTextColor(hdc, comctl32_color.clrBtnText);
3051 else
3053 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3055 SetBkMode(hdc, TRANSPARENT);
3056 textoutOptions &= ~ETO_OPAQUE;
3058 else
3060 SetBkMode(hdc, OPAQUE);
3061 SetBkColor(hdc, infoPtr->clrTextBk);
3064 SetTextColor(hdc, infoPtr->clrText);
3067 TRACE("drawing text %s, l=%d, t=%d, rect=(%d,%d)-(%d,%d)\n",
3068 debugstr_w(lvItem.pszText), textLeft, rcItem.top,
3069 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3070 ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
3071 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3073 if (Selected)
3075 /* fill in the gap */
3076 RECT rec;
3077 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3079 CopyRect(&rec,&rcItem);
3080 rec.left = rec.right;
3081 rec.right = rec.left+REPORT_MARGINX;
3082 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3083 &rec, NULL, 0, NULL);
3085 CopyRect(&rec,&rcItem);
3086 rec.right = rec.left;
3087 rec.left = rec.left - REPORT_MARGINX;
3088 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3089 &rec, NULL, 0, NULL);
3094 /***
3095 * DESCRIPTION:
3096 * Draws an item.
3098 * PARAMETER(S):
3099 * [I] infoPtr : valid pointer to the listview structure
3100 * [I] HDC : device context handle
3101 * [I] INT : item index
3102 * [I] RECT * : clipping rectangle
3104 * RETURN:
3105 * None
3107 static VOID LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3109 WCHAR szDispText[DISP_TEXT_SIZE];
3110 INT nLabelWidth;
3111 LVITEMW lvItem;
3112 INT nMixMode;
3113 DWORD dwBkColor;
3114 DWORD dwTextColor,dwTextX;
3115 BOOL bImage = FALSE;
3116 INT iBkMode = -1;
3117 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3118 RECT rcTemp;
3120 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
3123 /* get information needed for drawing the item */
3124 ZeroMemory(&lvItem, sizeof(lvItem));
3125 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3126 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3127 lvItem.iItem = nItem;
3128 lvItem.iSubItem = 0;
3129 lvItem.cchTextMax = DISP_TEXT_SIZE;
3130 lvItem.pszText = szDispText;
3131 *lvItem.pszText = '\0';
3132 LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE);
3133 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3135 /* redraw the background of the item */
3136 rcTemp = rcItem;
3137 if(infoPtr->nColumnCount == (nItem + 1))
3138 rcTemp.right = infoPtr->rcList.right;
3139 else
3140 rcTemp.right+=WIDTH_PADDING;
3142 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3144 /* do indent */
3145 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3147 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3149 if (SuggestedFocus)
3150 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3153 /* state icons */
3154 if (infoPtr->himlState != NULL)
3156 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3157 if (uStateImage > 0)
3159 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3160 rcItem.top, ILD_NORMAL);
3163 rcItem.left += infoPtr->iconSize.cx;
3164 if (SuggestedFocus)
3165 SuggestedFocus->left += infoPtr->iconSize.cx;
3166 bImage = TRUE;
3169 /* small icons */
3170 if (infoPtr->himlSmall != NULL)
3172 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) &&
3173 (lvItem.iImage>=0))
3175 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3176 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3177 rcItem.top, ILD_SELECTED);
3179 else if (lvItem.iImage>=0)
3181 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3182 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3183 rcItem.top, ILD_NORMAL);
3186 rcItem.left += infoPtr->iconSize.cx;
3188 if (SuggestedFocus)
3189 SuggestedFocus->left += infoPtr->iconSize.cx;
3190 bImage = TRUE;
3193 /* Don't bother painting item being edited */
3194 if (infoPtr->bEditing && lvItem.state & LVIS_FOCUSED && !FullSelect)
3195 return;
3197 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus))
3199 /* set item colors */
3200 dwBkColor = SetBkColor(hdc, comctl32_color.clrHighlight);
3201 dwTextColor = SetTextColor(hdc, comctl32_color.clrHighlightText);
3202 /* set raster mode */
3203 nMixMode = SetROP2(hdc, R2_XORPEN);
3205 else if ((GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3206 (lvItem.state & LVIS_SELECTED) && (!infoPtr->bFocus))
3208 dwBkColor = SetBkColor(hdc, comctl32_color.clr3dFace);
3209 dwTextColor = SetTextColor(hdc, comctl32_color.clrBtnText);
3210 /* set raster mode */
3211 nMixMode = SetROP2(hdc, R2_COPYPEN);
3213 else
3215 /* set item colors */
3216 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3218 dwBkColor = GetBkColor(hdc);
3219 iBkMode = SetBkMode(hdc, TRANSPARENT);
3220 textoutOptions &= ~ETO_OPAQUE;
3222 else
3224 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3225 iBkMode = SetBkMode(hdc, OPAQUE);
3228 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3229 /* set raster mode */
3230 nMixMode = SetROP2(hdc, R2_COPYPEN);
3233 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
3234 if (rcItem.left + nLabelWidth < rcItem.right)
3236 if (!FullSelect)
3237 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3238 if (bImage)
3239 rcItem.right += IMAGE_PADDING;
3242 /* draw label */
3243 dwTextX = rcItem.left + 1;
3244 if (bImage)
3245 dwTextX += IMAGE_PADDING;
3247 if (lvItem.pszText) {
3248 TRACE("drawing text rect=(%d,%d)-(%d,%d)\n",
3249 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3250 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3251 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3254 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3256 /* fill in the gap */
3257 RECT rec;
3258 CopyRect(&rec,&rcItem);
3259 rec.left = rec.right;
3260 rec.right = rec.left+REPORT_MARGINX;
3261 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3262 &rec, NULL, 0, NULL);
3265 if (!FullSelect)
3266 CopyRect(SuggestedFocus,&rcItem);
3268 if (nMixMode != 0)
3270 SetROP2(hdc, R2_COPYPEN);
3271 SetBkColor(hdc, dwBkColor);
3272 SetTextColor(hdc, dwTextColor);
3273 if (iBkMode != -1)
3274 SetBkMode(hdc, iBkMode);
3278 /***
3279 * DESCRIPTION:
3280 * Draws an item when in large icon display mode.
3282 * PARAMETER(S):
3283 * [I] infoPtr : valid pointer to the listview structure
3284 * [I] HDC : device context handle
3285 * [I] INT : item index
3286 * [I] RECT : clipping rectangle
3287 * [O] RECT * : The text rectangle about which to draw the focus
3289 * RETURN:
3290 * None
3292 static VOID LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem,
3293 RECT *SuggestedFocus)
3295 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3296 LVITEMW lvItem;
3297 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3298 DT_EDITCONTROL ;
3299 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3300 RECT rcTemp;
3302 TRACE("(hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3303 hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3305 /* get information needed for drawing the item */
3306 ZeroMemory(&lvItem, sizeof(lvItem));
3307 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3308 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3309 lvItem.iItem = nItem;
3310 lvItem.iSubItem = 0;
3311 lvItem.cchTextMax = DISP_TEXT_SIZE;
3312 lvItem.pszText = szDispText;
3313 *lvItem.pszText = '\0';
3314 LISTVIEW_GetItemW(infoPtr, &lvItem, FALSE);
3315 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3317 /* redraw the background of the item */
3318 rcTemp = rcItem;
3319 if(infoPtr->nColumnCount == (nItem + 1))
3320 rcTemp.right = infoPtr->rcList.right;
3321 else
3322 rcTemp.right+=WIDTH_PADDING;
3323 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3325 TRACE("background rect (%d,%d)-(%d,%d)\n",
3326 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3328 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3331 /* Figure out text colours etc. depending on state
3332 * At least the following states exist; there may be more.
3333 * Many items may be selected
3334 * At most one item may have the focus
3335 * The application may not actually be active currently
3336 * 1. The item is not selected in any way
3337 * 2. The cursor is flying over the icon or text and the text is being
3338 * expanded because it is not fully displayed currently.
3339 * 3. The item is selected and is focussed, i.e. the user has not clicked
3340 * in the blank area of the window, and the window (or application?)
3341 * still has the focus.
3342 * 4. As 3 except that a different window has the focus
3343 * 5. The item is the selected item of all the items, but the user has
3344 * clicked somewhere else on the window.
3345 * Only a few of these are handled currently. In particular 2 is not yet
3346 * handled since we do not support the functionality currently (or at least
3347 * we didn't when I wrote this)
3350 if (lvItem.state & LVIS_SELECTED)
3352 /* set item colors */
3353 SetBkColor(hdc, comctl32_color.clrHighlight);
3354 SetTextColor(hdc, comctl32_color.clrHighlightText);
3355 SetBkMode (hdc, OPAQUE);
3356 /* set raster mode */
3357 SetROP2(hdc, R2_XORPEN);
3358 /* When exactly is it in XOR? while being dragged? */
3360 else
3362 /* set item colors */
3363 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3365 SetBkMode(hdc, TRANSPARENT);
3367 else
3369 SetBkMode(hdc, OPAQUE);
3370 SetBkColor(hdc, infoPtr->clrTextBk);
3373 SetTextColor(hdc, infoPtr->clrText);
3374 /* set raster mode */
3375 SetROP2(hdc, R2_COPYPEN);
3378 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3379 * wrapping and long words split.
3380 * In cases 1 and 4 only a portion of the text is displayed with word
3381 * wrapping and both word and end ellipsis. (I don't yet know about path
3382 * ellipsis)
3384 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3385 DT_NOCLIP
3387 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3389 /* draw the icon */
3390 if (infoPtr->himlNormal != NULL)
3392 if (lvItem.iImage >= 0)
3394 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3395 rcItem.top,
3396 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3400 /* Draw the text below the icon */
3402 /* Don't bother painting item being edited */
3403 if ((infoPtr->bEditing && (lvItem.state & LVIS_FOCUSED)) ||
3404 !lstrlenW(lvItem.pszText))
3406 SetRectEmpty(SuggestedFocus);
3407 return;
3410 /* Since rcItem.left is left point of icon, compute left point of item box */
3411 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3412 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3413 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3414 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3415 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3416 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3417 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3418 infoPtr->rcList.left, infoPtr->rcList.top,
3419 infoPtr->rcList.right, infoPtr->rcList.bottom,
3420 infoPtr->rcView.left, infoPtr->rcView.top,
3421 infoPtr->rcView.right, infoPtr->rcView.bottom);
3423 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3424 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3427 /* draw label */
3429 /* I am sure of most of the uFormat values. However I am not sure about
3430 * whether we need or do not need the following:
3431 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3432 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3433 * We certainly do not need
3434 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3435 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3438 /* If the text is being drawn without clipping (i.e. the full text) then we
3439 * need to jump through a few hoops to ensure that it all gets displayed and
3440 * that the background is complete
3442 if (uFormat & DT_NOCLIP)
3444 RECT rcBack=rcItem;
3445 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3446 int dx, dy, old_wid, new_wid;
3447 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3448 /* Microsoft, in their great wisdom, have decided that the rectangle
3449 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3450 * not the location. So we have to do the centring ourselves (and take
3451 * responsibility for agreeing off-by-one consistency with them).
3453 old_wid = rcItem.right-rcItem.left;
3454 new_wid = rcBack.right - rcBack.left;
3455 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3456 dy = rcBack.top - rcItem.top;
3457 OffsetRect (&rcItem, dx, dy);
3458 FillRect(hdc, &rcItem, hBrush);
3459 DeleteObject(hBrush);
3461 /* else ? What if we are losing the focus? will we not get a complete
3462 * background?
3464 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3466 CopyRect(SuggestedFocus, &rcItem);
3469 /***
3470 * DESCRIPTION:
3471 * Draws listview items when in report display mode.
3473 * PARAMETER(S):
3474 * [I] infoPtr : valid pointer to the listview structure
3475 * [I] HDC : device context handle
3477 * RETURN:
3478 * None
3480 static VOID LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3482 SCROLLINFO scrollInfo;
3483 INT nDrawPosY = infoPtr->rcList.top;
3484 INT nColumnCount;
3485 RECT rcItem, rcTemp;
3486 INT j;
3487 INT nItem;
3488 INT nLast;
3489 BOOL FullSelected;
3490 DWORD cditemmode = CDRF_DODEFAULT;
3491 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
3492 INT scrollOffset;
3494 TRACE("\n");
3495 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3496 scrollInfo.cbSize = sizeof(SCROLLINFO);
3497 scrollInfo.fMask = SIF_POS;
3499 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3501 /* add 1 for displaying a partial item at the bottom */
3502 nLast = nItem + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
3503 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3505 /* send cache hint notification */
3506 if (lStyle & LVS_OWNERDATA)
3508 NMLVCACHEHINT nmlv;
3510 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
3511 nmlv.hdr.idFrom = GetWindowLongW(infoPtr->hwndSelf,GWL_ID);
3512 nmlv.hdr.code = LVN_ODCACHEHINT;
3513 nmlv.iFrom = nItem;
3514 nmlv.iTo = nLast;
3516 SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3517 (LPARAM)&nmlv);
3520 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3521 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3522 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3524 /* clear the background of any part of the control that doesn't contain items */
3525 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3526 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3528 /* nothing to draw */
3529 if(GETITEMCOUNT(infoPtr) == 0)
3530 return;
3532 /* Get scroll bar info once before loop */
3533 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
3534 scrollOffset = scrollInfo.nPos;
3536 for (; nItem < nLast; nItem++)
3538 RECT SuggestedFocusRect;
3540 /* Do Owner Draw */
3541 if (lStyle & LVS_OWNERDRAWFIXED)
3543 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3544 DRAWITEMSTRUCT dis;
3545 LVITEMW item;
3546 RECT br;
3548 TRACE("Owner Drawn\n");
3549 dis.CtlType = ODT_LISTVIEW;
3550 dis.CtlID = uID;
3551 dis.itemID = nItem;
3552 dis.itemAction = ODA_DRAWENTIRE;
3553 dis.itemState = 0;
3555 if (LISTVIEW_IsSelected(infoPtr,nItem)) dis.itemState|=ODS_SELECTED;
3556 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3558 dis.hwndItem = infoPtr->hwndSelf;
3559 dis.hDC = hdc;
3561 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3563 dis.rcItem.left = -scrollOffset;
3564 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3565 dis.rcItem.top = nDrawPosY;
3566 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3568 ZeroMemory(&item,sizeof(item));
3569 item.iItem = nItem;
3570 item.mask = LVIF_PARAM;
3571 ListView_GetItemW(infoPtr->hwndSelf, &item);
3573 dis.itemData = item.lParam;
3575 if (SendMessageW(GetParent(infoPtr->hwndSelf),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3577 nDrawPosY += infoPtr->nItemHeight;
3578 continue;
3582 if (FullSelected)
3584 RECT ir,br;
3586 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3587 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3589 ir.left += REPORT_MARGINX;
3590 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3591 ir.top = nDrawPosY;
3592 ir.bottom = ir.top + infoPtr->nItemHeight;
3594 CopyRect(&SuggestedFocusRect,&ir);
3597 for (j = 0; j < nColumnCount; j++)
3599 if (cdmode & CDRF_NOTIFYITEMDRAW)
3600 cditemmode = LISTVIEW_SendCustomDrawItemNotify (infoPtr, hdc, nItem, j,
3601 CDDS_ITEMPREPAINT);
3602 if (cditemmode & CDRF_SKIPDEFAULT)
3603 continue;
3605 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3607 rcItem.left += REPORT_MARGINX;
3608 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3609 rcItem.top = nDrawPosY;
3610 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3612 /* Offset the Scroll Bar Pos */
3613 rcItem.left -= scrollOffset;
3614 rcItem.right -= scrollOffset;
3616 if (j == 0)
3618 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem, FullSelected,
3619 &SuggestedFocusRect);
3621 else
3623 LISTVIEW_DrawSubItem(infoPtr, hdc, nItem, j, rcItem, FullSelected);
3626 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3627 LISTVIEW_SendCustomDrawItemNotify(infoPtr, hdc, nItem, 0,
3628 CDDS_ITEMPOSTPAINT);
3631 * Draw Focus Rect
3633 if (LISTVIEW_GetItemState(infoPtr,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3635 BOOL rop=FALSE;
3636 if (FullSelected && LISTVIEW_GetItemState(infoPtr,nItem,LVIS_SELECTED))
3637 rop = SetROP2(hdc, R2_XORPEN);
3639 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3640 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3642 if (rop)
3643 SetROP2(hdc, R2_COPYPEN);
3645 nDrawPosY += infoPtr->nItemHeight;
3649 /***
3650 * DESCRIPTION:
3651 * Retrieves the number of items that can fit vertically in the client area.
3653 * PARAMETER(S):
3654 * [I] infoPtr : valid pointer to the listview structure
3656 * RETURN:
3657 * Number of items per row.
3659 static INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
3661 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
3662 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3663 INT nCountPerRow = 1;
3665 if (nListWidth > 0)
3667 if (uView != LVS_REPORT)
3669 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3670 if (nCountPerRow == 0) nCountPerRow = 1;
3674 return nCountPerRow;
3677 /***
3678 * DESCRIPTION:
3679 * Retrieves the number of items that can fit horizontally in the client
3680 * area.
3682 * PARAMETER(S):
3683 * [I] infoPtr : valid pointer to the listview structure
3685 * RETURN:
3686 * Number of items per column.
3688 static INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
3690 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3691 INT nCountPerColumn = 1;
3693 if (nListHeight > 0)
3695 TRACE("rcList=(%d,%d)-(%d,%d), nItemHeight=%d, CYHSCROLL=%d\n",
3696 infoPtr->rcList.left,infoPtr->rcList.top,
3697 infoPtr->rcList.right,infoPtr->rcList.bottom,
3698 infoPtr->nItemHeight,
3699 GetSystemMetrics(SM_CYHSCROLL));
3700 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3701 if (nCountPerColumn == 0) nCountPerColumn = 1;
3704 return nCountPerColumn;
3707 /***
3708 * DESCRIPTION:
3709 * Retrieves the number of columns needed to display all the items when in
3710 * list display mode.
3712 * PARAMETER(S):
3713 * [I] infoPtr : valid pointer to the listview structure
3715 * RETURN:
3716 * Number of columns.
3718 static INT LISTVIEW_GetColumnCount(LISTVIEW_INFO *infoPtr)
3720 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
3721 INT nColumnCount = 0;
3723 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3725 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3726 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3729 return nColumnCount;
3733 /***
3734 * DESCRIPTION:
3735 * Draws listview items when in list display mode.
3737 * PARAMETER(S):
3738 * [I] infoPtr : valid pointer to the listview structure
3739 * [I] HDC : device context handle
3741 * RETURN:
3742 * None
3744 static VOID LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3746 RECT rcItem, FocusRect, rcTemp;
3747 INT i, j;
3748 INT nItem;
3749 INT nColumnCount;
3750 INT nCountPerColumn;
3751 INT nItemWidth = infoPtr->nItemWidth;
3752 INT nItemHeight = infoPtr->nItemHeight;
3753 DWORD cditemmode = CDRF_DODEFAULT;
3755 /* get number of fully visible columns */
3756 nColumnCount = LISTVIEW_GetColumnCount(infoPtr);
3757 infoPtr->nColumnCount = nColumnCount;
3758 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
3759 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3760 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3761 nColumnCount, nCountPerColumn, nItem);
3763 /* paint the background of the control that doesn't contain any items */
3764 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3765 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3767 /* nothing to draw, return here */
3768 if(GETITEMCOUNT(infoPtr) == 0)
3769 return;
3771 for (i = 0; i < nColumnCount; i++)
3773 for (j = 0; j < nCountPerColumn; j++, nItem++)
3775 if (nItem >= GETITEMCOUNT(infoPtr))
3776 return;
3778 if (cdmode & CDRF_NOTIFYITEMDRAW)
3779 cditemmode = LISTVIEW_SendCustomDrawItemNotify (infoPtr, hdc, nItem, 0,
3780 CDDS_ITEMPREPAINT);
3781 if (cditemmode & CDRF_SKIPDEFAULT)
3782 continue;
3784 rcItem.top = j * nItemHeight;
3785 rcItem.left = i * nItemWidth;
3786 rcItem.bottom = rcItem.top + nItemHeight;
3787 rcItem.right = rcItem.left + nItemWidth;
3788 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem, FALSE, &FocusRect);
3790 * Draw Focus Rect
3792 if (LISTVIEW_GetItemState(infoPtr,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3793 Rectangle(hdc, FocusRect.left, FocusRect.top,
3794 FocusRect.right,FocusRect.bottom);
3796 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3797 LISTVIEW_SendCustomDrawItemNotify(infoPtr, hdc, nItem, 0,
3798 CDDS_ITEMPOSTPAINT);
3804 /***
3805 * DESCRIPTION:
3806 * Draws listview items when in icon or small icon display mode.
3808 * PARAMETER(S):
3809 * [I] infoPtr : valid pointer to the listview structure
3810 * [I] HDC : device context handle
3812 * RETURN:
3813 * None
3815 static VOID LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL bSmall, DWORD cdmode)
3817 POINT ptPosition;
3818 POINT ptOrigin;
3819 RECT rcItem, SuggestedFocus, rcTemp;
3820 INT i;
3821 DWORD cditemmode = CDRF_DODEFAULT;
3823 TRACE("\n");
3824 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3825 /* DrawItem from erasing the incorrect background area */
3827 /* paint the background of the control that doesn't contain any items */
3828 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3829 LISTVIEW_FillBackground(infoPtr, hdc, &rcTemp);
3831 /* nothing to draw, return here */
3832 if(GETITEMCOUNT(infoPtr) == 0)
3833 return;
3835 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
3836 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3838 if (cdmode & CDRF_NOTIFYITEMDRAW)
3839 cditemmode = LISTVIEW_SendCustomDrawItemNotify (infoPtr, hdc, i, 0,
3840 CDDS_ITEMPREPAINT);
3841 if (cditemmode & CDRF_SKIPDEFAULT)
3842 continue;
3844 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3845 ptPosition.x += ptOrigin.x;
3846 ptPosition.y += ptOrigin.y;
3848 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3850 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3852 if (ptPosition.y < infoPtr->rcList.bottom)
3854 if (ptPosition.x < infoPtr->rcList.right)
3856 rcItem.top = ptPosition.y;
3857 rcItem.left = ptPosition.x;
3858 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3859 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3860 if (bSmall)
3861 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem, FALSE, &SuggestedFocus);
3862 else
3863 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem, &SuggestedFocus);
3865 * Draw Focus Rect
3867 if (LISTVIEW_GetItemState(infoPtr,i,LVIS_FOCUSED) &&
3868 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3869 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3870 SuggestedFocus.right,SuggestedFocus.bottom);
3875 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3876 LISTVIEW_SendCustomDrawItemNotify(infoPtr, hdc, i, 0,
3877 CDDS_ITEMPOSTPAINT);
3881 /***
3882 * DESCRIPTION:
3883 * Draws listview items.
3885 * PARAMETER(S):
3886 * [I] infoPtr : valid pointer to the listview structure
3887 * [I] HDC : device context handle
3889 * RETURN:
3890 * NoneX
3892 static VOID LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3894 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
3895 HFONT hOldFont;
3896 HPEN hPen, hOldPen;
3897 DWORD cdmode;
3898 RECT rect;
3900 infoPtr->bIsDrawing = TRUE;
3901 LISTVIEW_DumpListview (infoPtr, __LINE__);
3903 GetClientRect(infoPtr->hwndSelf, &rect);
3904 cdmode = LISTVIEW_SendCustomDrawNotify(infoPtr,CDDS_PREPAINT,hdc,rect);
3906 if (cdmode == CDRF_SKIPDEFAULT) return;
3908 /* select font */
3909 hOldFont = SelectObject(hdc, infoPtr->hFont);
3911 /* select the dotted pen (for drawing the focus box) */
3912 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3913 hOldPen = SelectObject(hdc, hPen);
3915 /* select transparent brush (for drawing the focus box) */
3916 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3918 TRACE("\n");
3919 if (uView == LVS_LIST)
3920 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3921 else if (uView == LVS_REPORT)
3922 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3923 else if (uView == LVS_SMALLICON)
3924 LISTVIEW_RefreshIcon(infoPtr, hdc, TRUE, cdmode);
3925 else if (uView == LVS_ICON)
3926 LISTVIEW_RefreshIcon(infoPtr, hdc, FALSE, cdmode);
3928 /* unselect objects */
3929 SelectObject(hdc, hOldFont);
3930 SelectObject(hdc, hOldPen);
3932 /* delete pen */
3933 DeleteObject(hPen);
3935 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3936 LISTVIEW_SendCustomDrawNotify(infoPtr, CDDS_POSTPAINT, hdc, rect);
3938 infoPtr->bIsDrawing = FALSE;
3942 /***
3943 * DESCRIPTION:
3944 * Calculates the approximate width and height of a given number of items.
3946 * PARAMETER(S):
3947 * [I] infoPtr : valid pointer to the listview structure
3948 * [I] INT : number of items
3949 * [I] INT : width
3950 * [I] INT : height
3952 * RETURN:
3953 * Returns a DWORD. The width in the low word and the height in high word.
3955 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3956 WORD wWidth, WORD wHeight)
3958 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
3959 INT nItemCountPerColumn = 1;
3960 INT nColumnCount = 0;
3961 DWORD dwViewRect = 0;
3963 if (nItemCount == -1)
3964 nItemCount = GETITEMCOUNT(infoPtr);
3966 if (uView == LVS_LIST)
3968 if (wHeight == 0xFFFF)
3970 /* use current height */
3971 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3974 if (wHeight < infoPtr->nItemHeight)
3975 wHeight = infoPtr->nItemHeight;
3977 if (nItemCount > 0)
3979 if (infoPtr->nItemHeight > 0)
3981 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3982 if (nItemCountPerColumn == 0)
3983 nItemCountPerColumn = 1;
3985 if (nItemCount % nItemCountPerColumn != 0)
3986 nColumnCount = nItemCount / nItemCountPerColumn;
3987 else
3988 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3992 /* Microsoft padding magic */
3993 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3994 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3996 dwViewRect = MAKELONG(wWidth, wHeight);
3998 else if (uView == LVS_REPORT)
3999 FIXME("uView == LVS_REPORT: not implemented\n");
4000 else if (uView == LVS_SMALLICON)
4001 FIXME("uView == LVS_SMALLICON: not implemented\n");
4002 else if (uView == LVS_ICON)
4003 FIXME("uView == LVS_ICON: not implemented\n");
4005 return dwViewRect;
4008 /***
4009 * DESCRIPTION:
4010 * Arranges listview items in icon display mode.
4012 * PARAMETER(S):
4013 * [I] infoPtr : valid pointer to the listview structure
4014 * [I] INT : alignment code
4016 * RETURN:
4017 * SUCCESS : TRUE
4018 * FAILURE : FALSE
4020 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
4022 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
4023 BOOL bResult = FALSE;
4025 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4027 switch (nAlignCode)
4029 case LVA_ALIGNLEFT:
4030 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4031 break;
4032 case LVA_ALIGNTOP:
4033 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4034 break;
4035 case LVA_DEFAULT:
4036 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4037 break;
4038 case LVA_SNAPTOGRID:
4039 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4040 break;
4044 return bResult;
4047 /* << LISTVIEW_CreateDragImage >> */
4050 /***
4051 * DESCRIPTION:
4052 * Removes all listview items and subitems.
4054 * PARAMETER(S):
4055 * [I] infoPtr : valid pointer to the listview structure
4057 * RETURN:
4058 * SUCCESS : TRUE
4059 * FAILURE : FALSE
4061 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4063 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
4064 UINT uView = lStyle & LVS_TYPEMASK;
4065 LISTVIEW_ITEM *lpItem;
4066 LISTVIEW_SUBITEM *lpSubItem;
4067 NMLISTVIEW nmlv;
4068 BOOL bSuppress;
4069 BOOL bResult = FALSE;
4070 HDPA hdpaSubItems;
4072 TRACE("()\n");
4074 LISTVIEW_RemoveAllSelections(infoPtr);
4075 infoPtr->nSelectionMark=-1;
4076 infoPtr->nFocusedItem=-1;
4077 /* But we are supposed to leave nHotItem as is! */
4079 if (lStyle & LVS_OWNERDATA)
4081 infoPtr->hdpaItems->nItemCount = 0;
4082 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
4083 return TRUE;
4086 if (GETITEMCOUNT(infoPtr) > 0)
4088 INT i, j;
4090 /* send LVN_DELETEALLITEMS notification */
4091 /* verify if subsequent LVN_DELETEITEM notifications should be
4092 suppressed */
4093 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4094 nmlv.iItem = -1;
4095 bSuppress = listview_notify(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4097 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4099 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4100 if (hdpaSubItems != NULL)
4102 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4104 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4105 if (lpSubItem != NULL)
4107 /* free subitem string */
4108 if (is_textW(lpSubItem->pszText))
4109 COMCTL32_Free(lpSubItem->pszText);
4111 /* free subitem */
4112 COMCTL32_Free(lpSubItem);
4116 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4117 if (lpItem != NULL)
4119 if (!bSuppress)
4121 /* send LVN_DELETEITEM notification */
4122 nmlv.iItem = i;
4123 nmlv.lParam = lpItem->lParam;
4124 listview_notify(infoPtr, LVN_DELETEITEM, &nmlv);
4127 /* free item string */
4128 if (is_textW(lpItem->pszText))
4129 COMCTL32_Free(lpItem->pszText);
4131 /* free item */
4132 COMCTL32_Free(lpItem);
4135 DPA_Destroy(hdpaSubItems);
4139 /* reinitialize listview memory */
4140 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4142 /* align items (set position of each item) */
4143 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4145 if (lStyle & LVS_ALIGNLEFT)
4147 LISTVIEW_AlignLeft(infoPtr);
4149 else
4151 LISTVIEW_AlignTop(infoPtr);
4155 LISTVIEW_UpdateScroll(infoPtr);
4157 /* invalidate client area (optimization needed) */
4158 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
4161 return bResult;
4164 /***
4165 * DESCRIPTION:
4166 * Removes a column from the listview control.
4168 * PARAMETER(S):
4169 * [I] infoPtr : valid pointer to the listview structure
4170 * [I] INT : column index
4172 * RETURN:
4173 * SUCCESS : TRUE
4174 * FAILURE : FALSE
4176 static LRESULT LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4178 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
4179 UINT uOwnerData = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_OWNERDATA;
4180 BOOL bResult = FALSE;
4182 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4184 bResult = uOwnerData ? TRUE : LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4186 /* Need to reset the item width when deleting a column */
4187 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
4189 /* reset scroll parameters */
4190 if (uView == LVS_REPORT)
4192 /* update scrollbar(s) */
4193 LISTVIEW_UpdateScroll(infoPtr);
4195 /* refresh client area */
4196 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
4200 return bResult;
4203 /***
4204 * DESCRIPTION:
4205 * Removes an item from the listview control.
4207 * PARAMETER(S):
4208 * [I] infoPtr : valid pointer to the listview structure
4209 * [I] INT : item index
4211 * RETURN:
4212 * SUCCESS : TRUE
4213 * FAILURE : FALSE
4215 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4217 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
4218 UINT uView = lStyle & LVS_TYPEMASK;
4219 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
4220 NMLISTVIEW nmlv;
4221 BOOL bResult = FALSE;
4222 HDPA hdpaSubItems;
4223 LISTVIEW_ITEM *lpItem;
4224 LISTVIEW_SUBITEM *lpSubItem;
4225 INT i;
4226 LVITEMW item;
4228 TRACE("(nItem=%d)\n", nItem);
4231 /* First, send LVN_DELETEITEM notification. */
4232 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4233 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
4234 nmlv.hdr.idFrom = lCtrlId;
4235 nmlv.hdr.code = LVN_DELETEITEM;
4236 nmlv.iItem = nItem;
4237 SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
4238 (LPARAM)&nmlv);
4241 /* remove it from the selection range */
4242 ZeroMemory(&item,sizeof(item));
4243 item.stateMask = LVIS_SELECTED;
4244 LISTVIEW_SetItemState(infoPtr,nItem,&item);
4246 if (lStyle & LVS_OWNERDATA)
4248 infoPtr->hdpaItems->nItemCount --;
4249 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
4250 return TRUE;
4253 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4255 /* initialize memory */
4256 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4258 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4259 if (hdpaSubItems != NULL)
4261 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4263 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4264 if (lpSubItem != NULL)
4266 /* free item string */
4267 if (is_textW(lpSubItem->pszText))
4268 COMCTL32_Free(lpSubItem->pszText);
4270 /* free item */
4271 COMCTL32_Free(lpSubItem);
4275 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4276 if (lpItem != NULL)
4278 /* free item string */
4279 if (is_textW(lpItem->pszText))
4280 COMCTL32_Free(lpItem->pszText);
4282 /* free item */
4283 COMCTL32_Free(lpItem);
4286 bResult = DPA_Destroy(hdpaSubItems);
4289 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
4291 /* align items (set position of each item) */
4292 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4294 if (lStyle & LVS_ALIGNLEFT)
4295 LISTVIEW_AlignLeft(infoPtr);
4296 else
4297 LISTVIEW_AlignTop(infoPtr);
4300 LISTVIEW_UpdateScroll(infoPtr);
4302 /* refresh client area */
4303 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
4306 return bResult;
4310 /***
4311 * DESCRIPTION:
4312 * Callback implementation for editlabel control
4314 * PARAMETER(S):
4315 * [I] infoPtr : valid pointer to the listview structure
4316 * [I] pszText : modified text
4317 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4319 * RETURN:
4320 * SUCCESS : TRUE
4321 * FAILURE : FALSE
4323 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4325 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
4326 NMLVDISPINFOW dispInfo;
4327 LISTVIEW_ITEM *lpItem;
4328 HDPA hdpaSubItems;
4329 LISTVIEW_ITEM lvItemRef;
4330 LVITEMW item;
4331 BOOL bResult = TRUE;
4332 INT nItem = infoPtr->nEditLabelItem;
4334 TRACE("(pszText=%s, nItem=%d, isW=%d)\n", debugstr_t(pszText, isW), nItem, isW);
4336 infoPtr->bEditing = FALSE;
4337 if (!(lStyle & LVS_OWNERDATA))
4339 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4340 return FALSE;
4342 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4343 return FALSE;
4345 else
4347 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4348 ZeroMemory(&item,sizeof(item));
4349 item.iItem = nItem;
4350 item.iSubItem = 0;
4351 item.mask = LVIF_PARAM | LVIF_STATE;
4352 ListView_GetItemW(infoPtr->hwndSelf, &item);
4353 lvItemRef.state = item.state;
4354 lvItemRef.iImage = item.iImage;
4355 lvItemRef.lParam = item.lParam;
4356 lpItem = &lvItemRef;
4359 ZeroMemory(&dispInfo, sizeof(dispInfo));
4360 dispInfo.item.mask = 0;
4361 dispInfo.item.iItem = nItem;
4362 dispInfo.item.state = lpItem->state;
4363 dispInfo.item.stateMask = 0;
4364 dispInfo.item.pszText = pszText;
4365 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4366 dispInfo.item.iImage = lpItem->iImage;
4367 dispInfo.item.lParam = lpItem->lParam;
4369 /* Do we need to update the Item Text */
4370 if(dispinfo_notifyT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW))
4371 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4372 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4374 return bResult;
4377 /***
4378 * DESCRIPTION:
4379 * Begin in place editing of specified list view item
4381 * PARAMETER(S):
4382 * [I] infoPtr : valid pointer to the listview structure
4383 * [I] INT : item index
4384 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4386 * RETURN:
4387 * SUCCESS : TRUE
4388 * FAILURE : FALSE
4390 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4392 NMLVDISPINFOW dispInfo;
4393 RECT rect;
4394 LISTVIEW_ITEM *lpItem;
4395 HWND hedit;
4396 HDPA hdpaSubItems;
4397 WCHAR szDispText[DISP_TEXT_SIZE];
4398 LVITEMW lvItem;
4399 LISTVIEW_ITEM lvItemRef;
4400 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
4402 if (~GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_EDITLABELS)
4403 return FALSE;
4405 infoPtr->nEditLabelItem = nItem;
4407 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4409 /* Is the EditBox still there, if so remove it */
4410 if(infoPtr->hwndEdit != 0)
4412 SetFocus(infoPtr->hwndSelf);
4413 infoPtr->hwndEdit = 0;
4416 LISTVIEW_SetSelection(infoPtr, nItem);
4417 LISTVIEW_SetItemFocus(infoPtr, nItem);
4419 if (!(lStyle & LVS_OWNERDATA))
4421 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4422 return 0;
4424 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4425 return 0;
4427 else
4429 LVITEMW item;
4430 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4431 ZeroMemory(&item, sizeof(item));
4432 item.iItem = nItem;
4433 item.iSubItem = 0;
4434 item.mask = LVIF_PARAM | LVIF_STATE;
4435 ListView_GetItemW(infoPtr->hwndSelf, &item);
4436 lvItemRef.iImage = item.iImage;
4437 lvItemRef.state = item.state;
4438 lvItemRef.lParam = item.lParam;
4439 lpItem = &lvItemRef;
4442 /* get information needed for drawing the item */
4443 ZeroMemory(&lvItem, sizeof(lvItem));
4444 lvItem.mask = LVIF_TEXT;
4445 lvItem.iItem = nItem;
4446 lvItem.iSubItem = 0;
4447 lvItem.cchTextMax = DISP_TEXT_SIZE;
4448 lvItem.pszText = szDispText;
4449 *lvItem.pszText = '\0';
4450 LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, isW);
4452 ZeroMemory(&dispInfo, sizeof(dispInfo));
4453 dispInfo.item.mask = 0;
4454 dispInfo.item.iItem = nItem;
4455 dispInfo.item.state = lpItem->state;
4456 dispInfo.item.stateMask = 0;
4457 dispInfo.item.pszText = lvItem.pszText;
4458 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4459 dispInfo.item.iImage = lpItem->iImage;
4460 dispInfo.item.lParam = lpItem->lParam;
4462 if (dispinfo_notifyT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4463 return 0;
4465 rect.left = LVIR_LABEL;
4466 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect))
4467 return 0;
4469 if (!(hedit = CreateEditLabelT(infoPtr, szDispText, WS_VISIBLE,
4470 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW)))
4471 return 0;
4473 infoPtr->hwndEdit = hedit;
4475 ShowWindow(infoPtr->hwndEdit,SW_NORMAL);
4476 infoPtr->bEditing = TRUE;
4477 SetFocus(infoPtr->hwndEdit);
4478 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4479 return infoPtr->hwndEdit;
4483 /***
4484 * DESCRIPTION:
4485 * Ensures the specified item is visible, scrolling into view if necessary.
4487 * PARAMETER(S):
4488 * [I] infoPtr : valid pointer to the listview structure
4489 * [I] INT : item index
4490 * [I] BOOL : partially or entirely visible
4492 * RETURN:
4493 * SUCCESS : TRUE
4494 * FAILURE : FALSE
4496 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4498 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
4499 INT nScrollPosHeight = 0;
4500 INT nScrollPosWidth = 0;
4501 SCROLLINFO scrollInfo;
4502 RECT rcItem;
4503 BOOL bRedraw = FALSE;
4505 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4506 scrollInfo.cbSize = sizeof(SCROLLINFO);
4507 scrollInfo.fMask = SIF_POS;
4509 /* ALWAYS bPartial == FALSE, FOR NOW! */
4511 rcItem.left = LVIR_BOUNDS;
4512 if (LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem))
4514 if (rcItem.left < infoPtr->rcList.left)
4516 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4518 /* scroll left */
4519 bRedraw = TRUE;
4520 if (uView == LVS_LIST)
4522 nScrollPosWidth = infoPtr->nItemWidth;
4523 rcItem.left += infoPtr->rcList.left;
4525 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4527 nScrollPosWidth = 1;
4528 rcItem.left += infoPtr->rcList.left;
4531 /* When in LVS_REPORT view, the scroll position should
4532 not be updated. */
4533 if (nScrollPosWidth != 0)
4535 if (rcItem.left % nScrollPosWidth == 0)
4536 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4537 else
4538 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4540 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
4544 else if (rcItem.right > infoPtr->rcList.right)
4546 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4548 /* scroll right */
4549 bRedraw = TRUE;
4550 if (uView == LVS_LIST)
4552 rcItem.right -= infoPtr->rcList.right;
4553 nScrollPosWidth = infoPtr->nItemWidth;
4555 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4557 rcItem.right -= infoPtr->rcList.right;
4558 nScrollPosWidth = 1;
4561 /* When in LVS_REPORT view, the scroll position should
4562 not be updated. */
4563 if (nScrollPosWidth != 0)
4565 if (rcItem.right % nScrollPosWidth == 0)
4566 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4567 else
4568 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4570 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
4575 if (rcItem.top < infoPtr->rcList.top)
4577 /* scroll up */
4578 bRedraw = TRUE;
4579 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4581 if (uView == LVS_REPORT)
4583 rcItem.top -= infoPtr->rcList.top;
4584 nScrollPosHeight = infoPtr->nItemHeight;
4586 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4588 nScrollPosHeight = 1;
4589 rcItem.top += infoPtr->rcList.top;
4592 if (rcItem.top % nScrollPosHeight == 0)
4593 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4594 else
4595 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4597 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
4600 else if (rcItem.bottom > infoPtr->rcList.bottom)
4602 /* scroll down */
4603 bRedraw = TRUE;
4604 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4606 if (uView == LVS_REPORT)
4608 rcItem.bottom -= infoPtr->rcList.bottom;
4609 nScrollPosHeight = infoPtr->nItemHeight;
4611 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4613 nScrollPosHeight = 1;
4614 rcItem.bottom -= infoPtr->rcList.bottom;
4617 if (rcItem.bottom % nScrollPosHeight == 0)
4618 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4619 else
4620 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4622 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
4627 if(bRedraw)
4628 InvalidateRect(infoPtr->hwndSelf,NULL,TRUE);
4629 return TRUE;
4632 /***
4633 * DESCRIPTION:
4634 * Retrieves the nearest item, given a position and a direction.
4636 * PARAMETER(S):
4637 * [I] infoPtr : valid pointer to the listview structure
4638 * [I] POINT : start position
4639 * [I] UINT : direction
4641 * RETURN:
4642 * Item index if successdful, -1 otherwise.
4644 static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDirection)
4646 LV_INTHIT lvIntHit;
4647 INT nItem = -1;
4648 RECT rcView;
4650 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4651 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4652 ((vkDirection == VK_UP) ? "VK_UP" :
4653 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4655 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
4657 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4658 LISTVIEW_GetOrigin(infoPtr, &lvIntHit.ht.pt);
4659 lvIntHit.ht.pt.x += pt.x;
4660 lvIntHit.ht.pt.y += pt.y;
4662 if (vkDirection == VK_DOWN)
4663 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4664 else if (vkDirection == VK_UP)
4665 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4666 else if (vkDirection == VK_LEFT)
4667 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4668 else if (vkDirection == VK_RIGHT)
4669 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4671 if (!PtInRect(&rcView, lvIntHit.ht.pt))
4672 return -1;
4673 else
4675 nItem = LISTVIEW_SuperHitTestItem(infoPtr, &lvIntHit, TRUE);
4676 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4680 return nItem;
4683 /***
4684 * DESCRIPTION:
4685 * Searches for an item with specific characteristics.
4687 * PARAMETER(S):
4688 * [I] hwnd : window handle
4689 * [I] nStart : base item index
4690 * [I] lpFindInfo : item information to look for
4692 * RETURN:
4693 * SUCCESS : index of item
4694 * FAILURE : -1
4696 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4697 LPLVFINDINFOW lpFindInfo)
4699 POINT ptItem;
4700 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4701 LVITEMW lvItem;
4702 BOOL bWrap = FALSE;
4703 INT nItem = nStart;
4704 INT nLast = GETITEMCOUNT(infoPtr);
4706 if ((nItem >= -1) && (lpFindInfo != NULL))
4708 ZeroMemory(&lvItem, sizeof(lvItem));
4710 if (lpFindInfo->flags & LVFI_PARAM)
4712 lvItem.mask |= LVIF_PARAM;
4715 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4717 lvItem.mask |= LVIF_TEXT;
4718 lvItem.pszText = szDispText;
4719 lvItem.cchTextMax = DISP_TEXT_SIZE;
4722 if (lpFindInfo->flags & LVFI_WRAP)
4723 bWrap = TRUE;
4725 if (lpFindInfo->flags & LVFI_NEARESTXY)
4727 ptItem.x = lpFindInfo->pt.x;
4728 ptItem.y = lpFindInfo->pt.y;
4731 while (1)
4733 while (nItem < nLast)
4735 if (lpFindInfo->flags & LVFI_NEARESTXY)
4737 nItem = LISTVIEW_GetNearestItem(infoPtr, ptItem,
4738 lpFindInfo->vkDirection);
4739 if (nItem != -1)
4741 /* get position of the new item index */
4742 if (!ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &ptItem))
4743 return -1;
4745 else
4746 return -1;
4748 else
4750 nItem++;
4753 lvItem.iItem = nItem;
4754 lvItem.iSubItem = 0;
4755 if (LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE))
4757 if (lvItem.mask & LVIF_TEXT)
4759 if (lpFindInfo->flags & LVFI_PARTIAL)
4761 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4762 continue;
4764 else
4766 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4767 continue;
4771 if (lvItem.mask & LVIF_PARAM)
4773 if (lpFindInfo->lParam != lvItem.lParam)
4774 continue;
4777 return nItem;
4781 if (bWrap)
4783 nItem = -1;
4784 nLast = nStart + 1;
4785 bWrap = FALSE;
4787 else
4789 return -1;
4794 return -1;
4797 /***
4798 * DESCRIPTION:
4799 * Searches for an item with specific characteristics.
4801 * PARAMETER(S):
4802 * [I] hwnd : window handle
4803 * [I] nStart : base item index
4804 * [I] lpFindInfo : item information to look for
4806 * RETURN:
4807 * SUCCESS : index of item
4808 * FAILURE : -1
4810 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4811 LPLVFINDINFOA lpFindInfo)
4813 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4814 LVFINDINFOW fiw;
4815 LRESULT res;
4817 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4818 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4819 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4820 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4821 return res;
4824 /***
4825 * DESCRIPTION:
4826 * Retrieves the background image of the listview control.
4828 * PARAMETER(S):
4829 * [I] infoPtr : valid pointer to the listview structure
4830 * [O] LPLVMKBIMAGE : background image attributes
4832 * RETURN:
4833 * SUCCESS : TRUE
4834 * FAILURE : FALSE
4836 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4837 /* { */
4838 /* FIXME (listview, "empty stub!\n"); */
4839 /* return FALSE; */
4840 /* } */
4842 /***
4843 * DESCRIPTION:
4844 * Retrieves the callback mask.
4846 * PARAMETER(S):
4847 * [I] infoPtr : valid pointer to the listview structure
4849 * RETURN:
4850 * Value of mask
4852 static UINT LISTVIEW_GetCallbackMask(LISTVIEW_INFO *infoPtr)
4854 return infoPtr->uCallbackMask;
4857 /***
4858 * DESCRIPTION:
4859 * Retrieves column attributes.
4861 * PARAMETER(S):
4862 * [I] infoPtr : valid pointer to the listview structure
4863 * [I] INT : column index
4864 * [IO] LPLVCOLUMNW : column information
4865 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4866 * otherwise it is in fact a LPLVCOLUMNA
4868 * RETURN:
4869 * SUCCESS : TRUE
4870 * FAILURE : FALSE
4872 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4874 HDITEMW hdi;
4875 BOOL bResult = FALSE;
4877 if (lpColumn != NULL)
4880 /* initialize memory */
4881 ZeroMemory(&hdi, sizeof(hdi));
4883 if (lpColumn->mask & LVCF_FMT)
4884 hdi.mask |= HDI_FORMAT;
4886 if (lpColumn->mask & LVCF_WIDTH)
4887 hdi.mask |= HDI_WIDTH;
4889 if (lpColumn->mask & LVCF_TEXT)
4891 hdi.mask |= HDI_TEXT;
4892 hdi.cchTextMax = lpColumn->cchTextMax;
4893 hdi.pszText = lpColumn->pszText;
4896 if (lpColumn->mask & LVCF_IMAGE)
4897 hdi.mask |= HDI_IMAGE;
4899 if (lpColumn->mask & LVCF_ORDER)
4900 hdi.mask |= HDI_ORDER;
4902 if (isW)
4903 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4904 else
4905 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4907 if (bResult)
4909 if (lpColumn->mask & LVCF_FMT)
4911 lpColumn->fmt = 0;
4913 if (hdi.fmt & HDF_LEFT)
4914 lpColumn->fmt |= LVCFMT_LEFT;
4915 else if (hdi.fmt & HDF_RIGHT)
4916 lpColumn->fmt |= LVCFMT_RIGHT;
4917 else if (hdi.fmt & HDF_CENTER)
4918 lpColumn->fmt |= LVCFMT_CENTER;
4920 if (hdi.fmt & HDF_IMAGE)
4921 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4923 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4924 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4927 if (lpColumn->mask & LVCF_WIDTH)
4928 lpColumn->cx = hdi.cxy;
4930 if (lpColumn->mask & LVCF_IMAGE)
4931 lpColumn->iImage = hdi.iImage;
4933 if (lpColumn->mask & LVCF_ORDER)
4934 lpColumn->iOrder = hdi.iOrder;
4936 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4937 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4942 return bResult;
4946 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4948 INT i;
4950 if (!lpiArray)
4951 return FALSE;
4953 /* FIXME: little hack */
4954 for (i = 0; i < iCount; i++)
4955 lpiArray[i] = i;
4957 return TRUE;
4960 /***
4961 * DESCRIPTION:
4962 * Retrieves the column width.
4964 * PARAMETER(S):
4965 * [I] infoPtr : valid pointer to the listview structure
4966 * [I] int : column index
4968 * RETURN:
4969 * SUCCESS : column width
4970 * FAILURE : zero
4972 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4974 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
4975 INT nColumnWidth = 0;
4976 HDITEMW hdi;
4978 if (uView == LVS_LIST)
4980 nColumnWidth = infoPtr->nItemWidth;
4982 else if (uView == LVS_REPORT)
4984 /* get column width from header */
4985 ZeroMemory(&hdi, sizeof(hdi));
4986 hdi.mask = HDI_WIDTH;
4987 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4988 nColumnWidth = hdi.cxy;
4991 return nColumnWidth;
4994 /***
4995 * DESCRIPTION:
4996 * In list or report display mode, retrieves the number of items that can fit
4997 * vertically in the visible area. In icon or small icon display mode,
4998 * retrieves the total number of visible items.
5000 * PARAMETER(S):
5001 * [I] infoPtr : valid pointer to the listview structure
5003 * RETURN:
5004 * Number of fully visible items.
5006 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5008 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
5009 INT nItemCount = 0;
5011 if (uView == LVS_LIST)
5013 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5015 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
5016 LISTVIEW_GetCountPerColumn(infoPtr);
5019 else if (uView == LVS_REPORT)
5021 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
5023 else
5025 nItemCount = GETITEMCOUNT(infoPtr);
5028 return nItemCount;
5032 /***
5033 * DESCRIPTION:
5034 * Retrieves an image list handle.
5036 * PARAMETER(S):
5037 * [I] infoPtr : valid pointer to the listview structure
5038 * [I] INT : image list identifier
5040 * RETURN:
5041 * SUCCESS : image list handle
5042 * FAILURE : NULL
5044 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5046 HIMAGELIST himl = NULL;
5048 switch (nImageList)
5050 case LVSIL_NORMAL:
5051 himl = infoPtr->himlNormal;
5052 break;
5053 case LVSIL_SMALL:
5054 himl = infoPtr->himlSmall;
5055 break;
5056 case LVSIL_STATE:
5057 himl = infoPtr->himlState;
5058 break;
5061 return (LRESULT)himl;
5064 /* LISTVIEW_GetISearchString */
5066 /***
5067 * DESCRIPTION:
5068 * Retrieves item attributes.
5070 * PARAMETER(S):
5071 * [I] hwnd : window handle
5072 * [IO] lpLVItem : item info
5073 * [I] internal : if true then we will use tricks that avoid copies
5074 * but are not compatible with the regular interface
5075 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5076 * if FALSE, the lpLVItem is a LPLVITEMA.
5078 * RETURN:
5079 * SUCCESS : TRUE
5080 * FAILURE : FALSE
5082 static LRESULT LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5084 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
5085 NMLVDISPINFOW dispInfo;
5086 LISTVIEW_SUBITEM *lpSubItem;
5087 LISTVIEW_ITEM *lpItem;
5088 HDPA hdpaSubItems;
5089 void* null = NULL;
5090 INT* piImage = (INT*)&null;
5091 LPWSTR* ppszText= (LPWSTR*)&null;
5092 LPARAM* plParam = (LPARAM*)&null;
5094 if (internal && !isW)
5096 ERR("We can't have internal non-Unicode GetItem!\n");
5097 return FALSE;
5100 /* In the following:
5101 * lpLVItem describes the information requested by the user
5102 * lpItem/lpSubItem is what we have
5103 * dispInfo is a structure we use to request the missing
5104 * information from the application
5107 TRACE("(lpLVItem=%s, internal=%d, isW=%d)\n",
5108 debuglvitem_t(lpLVItem, isW), internal, isW);
5110 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5111 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5112 return FALSE;
5114 ZeroMemory(&dispInfo, sizeof(dispInfo));
5116 if (lStyle & LVS_OWNERDATA)
5118 if (lpLVItem->mask & ~LVIF_STATE)
5120 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5121 dispinfo_notifyT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5122 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5123 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5126 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5128 lpLVItem->state = 0;
5129 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5130 lpLVItem->state |= LVIS_FOCUSED;
5131 if (LISTVIEW_IsSelected(infoPtr,lpLVItem->iItem))
5132 lpLVItem->state |= LVIS_SELECTED;
5135 return TRUE;
5138 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5139 if (hdpaSubItems == NULL) return FALSE;
5141 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5142 return FALSE;
5144 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5145 if (lpLVItem->iSubItem == 0)
5147 piImage=&lpItem->iImage;
5148 ppszText=&lpItem->pszText;
5149 plParam=&lpItem->lParam;
5150 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5152 dispInfo.item.mask |= LVIF_STATE;
5153 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5156 else
5158 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5159 if (lpSubItem != NULL)
5161 piImage=&lpSubItem->iImage;
5162 ppszText=&lpSubItem->pszText;
5166 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5168 dispInfo.item.mask |= LVIF_IMAGE;
5171 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5173 dispInfo.item.mask |= LVIF_TEXT;
5174 dispInfo.item.pszText = lpLVItem->pszText;
5175 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5176 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5177 *dispInfo.item.pszText = '\0';
5178 if (dispInfo.item.pszText && (*ppszText == NULL))
5179 *dispInfo.item.pszText = '\0';
5182 if (dispInfo.item.mask != 0)
5184 /* We don't have all the requested info, query the application */
5185 dispInfo.item.iItem = lpLVItem->iItem;
5186 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5187 dispInfo.item.lParam = lpItem->lParam;
5188 dispinfo_notifyT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5189 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5192 if (dispInfo.item.mask & LVIF_IMAGE)
5194 lpLVItem->iImage = dispInfo.item.iImage;
5195 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5196 *piImage = dispInfo.item.iImage;
5198 else if (lpLVItem->mask & LVIF_IMAGE)
5200 lpLVItem->iImage = *piImage;
5203 if (dispInfo.item.mask & LVIF_PARAM)
5205 lpLVItem->lParam = dispInfo.item.lParam;
5206 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5207 *plParam = dispInfo.item.lParam;
5209 else if (lpLVItem->mask & LVIF_PARAM)
5210 lpLVItem->lParam = lpItem->lParam;
5212 if (dispInfo.item.mask & LVIF_TEXT)
5214 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5215 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5217 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5218 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5219 if (lpLVItem->pszText != dispInfo.item.pszText)
5220 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5223 else if (lpLVItem->mask & LVIF_TEXT)
5225 if (internal) lpLVItem->pszText = *ppszText;
5226 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5229 if (lpLVItem->iSubItem == 0)
5231 if (dispInfo.item.mask & LVIF_STATE)
5233 lpLVItem->state = lpItem->state;
5234 lpLVItem->state &= ~dispInfo.item.stateMask;
5235 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5237 lpLVItem->state &= ~LVIS_SELECTED;
5238 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5239 LISTVIEW_IsSelected(infoPtr,dispInfo.item.iItem))
5240 lpLVItem->state |= LVIS_SELECTED;
5242 else if (lpLVItem->mask & LVIF_STATE)
5244 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5246 lpLVItem->state &= ~LVIS_SELECTED;
5247 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5248 LISTVIEW_IsSelected(infoPtr,lpLVItem->iItem))
5249 lpLVItem->state |= LVIS_SELECTED;
5252 if (lpLVItem->mask & LVIF_PARAM)
5253 lpLVItem->lParam = lpItem->lParam;
5255 if (lpLVItem->mask & LVIF_INDENT)
5256 lpLVItem->iIndent = lpItem->iIndent;
5259 return TRUE;
5263 /***
5264 * DESCRIPTION:
5265 * Retrieves the rectangle enclosing the item icon and text.
5267 * PARAMETER(S):
5268 * [I] infoPtr : valid pointer to the listview structure
5269 * [I] INT : item index
5270 * [O] LPRECT : coordinate information
5272 * RETURN:
5273 * SUCCESS : TRUE
5274 * FAILURE : FALSE
5276 static BOOL LISTVIEW_GetItemBoundBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lpRect)
5278 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
5279 UINT uView = lStyle & LVS_TYPEMASK;
5280 BOOL bResult = FALSE;
5281 HDPA hdpaSubItems;
5282 LISTVIEW_ITEM *lpItem;
5283 INT nCountPerColumn;
5284 INT nRow;
5286 TRACE("(nItem=%d,lpRect=%p)\n", nItem, lpRect);
5288 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5289 (lpRect != NULL))
5291 if (uView == LVS_LIST)
5293 bResult = TRUE;
5294 nItem = nItem - ListView_GetTopIndex(infoPtr->hwndSelf);
5295 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5296 if (nItem < 0)
5298 nRow = nItem % nCountPerColumn;
5299 if (nRow == 0)
5301 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5302 lpRect->top = 0;
5304 else
5306 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5307 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5310 else
5312 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5313 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5316 else if (uView == LVS_REPORT)
5318 bResult = TRUE;
5319 lpRect->left = REPORT_MARGINX;
5320 lpRect->top = ((nItem - ListView_GetTopIndex(infoPtr->hwndSelf)) *
5321 infoPtr->nItemHeight) + infoPtr->rcList.top;
5323 if (!(lStyle & LVS_NOSCROLL))
5325 SCROLLINFO scrollInfo;
5326 /* Adjust position by scrollbar offset */
5327 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5328 scrollInfo.cbSize = sizeof(SCROLLINFO);
5329 scrollInfo.fMask = SIF_POS;
5330 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
5331 lpRect->left -= scrollInfo.nPos;
5334 else /* either LVS_ICON or LVS_SMALLICON */
5336 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5338 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5340 bResult = TRUE;
5341 lpRect->left = lpItem->ptPosition.x;
5342 lpRect->top = lpItem->ptPosition.y;
5347 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5348 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5349 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5350 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5351 return bResult;
5354 /***
5355 * DESCRIPTION:
5356 * Retrieves the position (upper-left) of the listview control item.
5357 * Note that for LVS_ICON style, the upper-left is that of the icon
5358 * and not the bounding box.
5360 * PARAMETER(S):
5361 * [I] infoPtr : valid pointer to the listview structure
5362 * [I] INT : item index
5363 * [O] LPPOINT : coordinate information
5365 * RETURN:
5366 * SUCCESS : TRUE
5367 * FAILURE : FALSE
5369 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5371 UINT uView = GetWindowLongA(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
5372 BOOL bResult = FALSE;
5373 RECT rcBounding;
5375 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5377 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5378 (lpptPosition != NULL))
5380 bResult = LISTVIEW_GetItemBoundBox(infoPtr, nItem, &rcBounding);
5381 lpptPosition->x = rcBounding.left;
5382 lpptPosition->y = rcBounding.top;
5383 if (uView == LVS_ICON)
5385 lpptPosition->y += ICON_TOP_PADDING;
5386 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5388 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5389 lpptPosition->x, lpptPosition->y);
5391 return bResult;
5394 /***
5395 * Update the bounding rectangle around the text under a large icon.
5396 * This depends on whether it has the focus or not.
5397 * On entry the rectangle's top, left and right should be set.
5398 * On return the bottom will also be set and the width may have been
5399 * modified.
5401 * PARAMETER
5402 * [I] infoPtr : pointer to the listview structure
5403 * [I] nItem : the item for which we are calculating this
5404 * [I] rect : the rectangle to be updated
5406 * This appears to be weird, even in the Microsoft implementation.
5408 static void LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *infoPtr, int nItem, RECT *rect)
5410 HDC hdc = GetDC (infoPtr->hwndSelf);
5411 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5413 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5415 /* We (aim to) display the full text. In Windows 95 it appears to
5416 * calculate the size assuming the specified font and then it draws
5417 * the text in that region with the specified font except scaled to
5418 * 10 point (or the height of the system font or ...). Thus if the
5419 * window has 24 point Helvetica the highlit rectangle will be
5420 * taller than the text and if it is 7 point Helvetica then the text
5421 * will be clipped.
5422 * For now we will simply say that it is the correct size to display
5423 * the text in the specified font.
5425 LVITEMW lvItem;
5426 lvItem.mask = LVIF_TEXT;
5427 lvItem.iItem = nItem;
5428 lvItem.iSubItem = 0;
5429 /* We will specify INTERNAL and so will receive back a const
5430 * pointer to the text, rather than specifying a buffer to which
5431 * to copy it.
5433 LISTVIEW_GetItemW (infoPtr, &lvItem, TRUE);
5434 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5435 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5436 DT_WORDBREAK | DT_NOPREFIX);
5437 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5439 else
5441 /* As far as I can see the text region seems to be trying to be
5442 * "tall enough for two lines of text". Once again (comctl32.dll ver
5443 * 5.81?) it measures this on the basis of the selected font and then
5444 * draws it with the same font except in 10 point size. This can lead
5445 * to more or less than the two rows appearing.
5446 * Question; are we supposed to be including DT_EXTERNALLEADING?
5447 * Question; should the width be shrunk to the space required to
5448 * display the two lines?
5450 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5453 SelectObject (hdc, hOldFont);
5454 ReleaseDC (infoPtr->hwndSelf, hdc);
5457 /***
5458 * DESCRIPTION:
5459 * Retrieves the bounding rectangle for a listview control item.
5461 * PARAMETER(S):
5462 * [I] infoPtr : valid pointer to the listview structure
5463 * [I] INT : item index
5464 * [IO] LPRECT : bounding rectangle coordinates
5465 * lprc->left specifies the portion of the item for which the bounding
5466 * rectangle will be retrieved.
5468 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5469 * including the icon and label.
5470 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5471 * LVIR_LABEL Returns the bounding rectangle of the item text.
5472 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5473 * rectangles, but excludes columns in report view.
5475 * RETURN:
5476 * SUCCESS : TRUE
5477 * FAILURE : FALSE
5479 * NOTES
5480 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5481 * upon whether the window has the focus currently and on whether the item
5482 * is the one with the focus. Ensure that the control's record of which
5483 * item has the focus agrees with the items' records.
5485 static LRESULT LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5487 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
5488 BOOL bResult = FALSE;
5489 POINT ptOrigin;
5490 POINT ptItem;
5491 INT nLeftPos;
5492 INT nLabelWidth;
5493 INT nIndent;
5494 LVITEMW lvItem;
5495 RECT rcInternal;
5497 TRACE("(nItem=%d, lprc=%p)\n", nItem, lprc);
5499 if (uView & LVS_REPORT)
5501 ZeroMemory(&lvItem, sizeof(lvItem));
5502 lvItem.mask = LVIF_INDENT;
5503 lvItem.iItem = nItem;
5504 lvItem.iSubItem = 0;
5505 LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE);
5507 /* do indent */
5508 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5509 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5510 else
5511 nIndent = 0;
5513 else
5514 nIndent = 0;
5516 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5518 switch(lprc->left)
5520 case LVIR_ICON:
5521 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptItem)) break;
5522 if (uView == LVS_ICON)
5524 if (infoPtr->himlNormal != NULL)
5526 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5528 bResult = TRUE;
5529 lprc->left = ptItem.x + ptOrigin.x;
5530 lprc->top = ptItem.y + ptOrigin.y;
5531 lprc->right = lprc->left + infoPtr->iconSize.cx;
5532 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5533 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5537 else if (uView == LVS_SMALLICON)
5539 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5541 bResult = TRUE;
5542 lprc->left = ptItem.x + ptOrigin.x;
5543 lprc->top = ptItem.y + ptOrigin.y;
5544 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5546 if (infoPtr->himlState != NULL)
5547 lprc->left += infoPtr->iconSize.cx;
5549 if (infoPtr->himlSmall != NULL)
5550 lprc->right = lprc->left + infoPtr->iconSize.cx;
5551 else
5552 lprc->right = lprc->left;
5555 else
5557 bResult = TRUE;
5558 lprc->left = ptItem.x;
5559 if (uView & LVS_REPORT)
5560 lprc->left += nIndent;
5561 lprc->top = ptItem.y;
5562 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5564 if (infoPtr->himlState != NULL)
5565 lprc->left += infoPtr->iconSize.cx;
5567 if (infoPtr->himlSmall != NULL)
5568 lprc->right = lprc->left + infoPtr->iconSize.cx;
5569 else
5570 lprc->right = lprc->left;
5572 break;
5574 case LVIR_LABEL:
5575 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptItem)) break;
5576 if (uView == LVS_ICON)
5578 if (infoPtr->himlNormal != NULL)
5580 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5582 bResult = TRUE;
5583 lprc->left = ptItem.x + ptOrigin.x;
5584 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5585 ICON_BOTTOM_PADDING);
5586 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5587 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5589 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5590 lprc->right = lprc->left + nLabelWidth;
5592 else
5594 lprc->left += 1;
5595 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5596 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, lprc);
5598 lprc->bottom = lprc->top + infoPtr->ntmHeight + HEIGHT_PADDING;
5602 else if (uView == LVS_SMALLICON)
5604 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5606 bResult = TRUE;
5607 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5608 lprc->top = ptItem.y + ptOrigin.y;
5609 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5611 if (infoPtr->himlState != NULL)
5612 lprc->left += infoPtr->iconSize.cx;
5614 if (infoPtr->himlSmall != NULL)
5615 lprc->left += infoPtr->iconSize.cx;
5617 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5618 nLabelWidth += TRAILING_PADDING;
5619 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5620 lprc->right = lprc->left + nLabelWidth;
5621 else
5622 lprc->right = nLeftPos + infoPtr->nItemWidth;
5625 else
5627 bResult = TRUE;
5628 if (uView == LVS_REPORT)
5629 nLeftPos = lprc->left = ptItem.x + nIndent;
5630 else
5631 nLeftPos = lprc->left = ptItem.x;
5632 lprc->top = ptItem.y;
5633 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5635 if (infoPtr->himlState != NULL)
5636 lprc->left += infoPtr->iconSize.cx;
5638 if (infoPtr->himlSmall != NULL)
5639 lprc->left += infoPtr->iconSize.cx;
5641 if (uView != LVS_REPORT)
5643 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5644 nLabelWidth += TRAILING_PADDING;
5645 if (infoPtr->himlSmall)
5646 nLabelWidth += IMAGE_PADDING;
5648 else
5649 nLabelWidth = LISTVIEW_GetColumnWidth(infoPtr, 0)-lprc->left;
5650 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5651 lprc->right = lprc->left + nLabelWidth;
5652 else
5653 lprc->right = nLeftPos + infoPtr->nItemWidth;
5655 break;
5657 case LVIR_BOUNDS:
5658 if (!LISTVIEW_GetItemBoundBox(infoPtr, nItem, &rcInternal)) break;
5659 ptItem.x = rcInternal.left;
5660 ptItem.y = rcInternal.top;
5661 if (uView == LVS_ICON)
5663 if (infoPtr->himlNormal != NULL)
5665 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5667 RECT label_rect;
5668 INT text_left, text_right, icon_left, text_pos_x;
5669 /* for style LVS_ICON bounds
5670 * left = min(icon.left, text.left)
5671 * right = max(icon.right, text.right)
5672 * top = boundbox.top + NOTHITABLE
5673 * bottom = text.bottom + 1
5675 bResult = TRUE;
5676 icon_left = text_left = ptItem.x;
5678 /* Correct ptItem to icon upper-left */
5679 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5680 ptItem.y += ICON_TOP_PADDING;
5682 /* Compute the label left and right */
5683 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5684 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5685 if (text_pos_x > 1)
5687 text_left += text_pos_x / 2;
5688 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5690 else
5692 text_left += 1;
5693 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5696 /* Compute rectangle w/o the text height */
5697 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5698 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5699 text_right) + ptOrigin.x;
5700 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5701 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5702 + infoPtr->iconSize.cy + 1
5703 + ICON_BOTTOM_PADDING;
5705 CopyRect (&label_rect, lprc);
5706 label_rect.top = lprc->bottom;
5707 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, &label_rect);
5708 UnionRect (lprc, lprc, &label_rect);
5712 else if (uView == LVS_SMALLICON)
5714 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5716 bResult = TRUE;
5717 lprc->left = ptItem.x + ptOrigin.x;
5718 lprc->right = lprc->left;
5719 lprc->top = ptItem.y + ptOrigin.y;
5720 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5721 if (infoPtr->himlState != NULL)
5722 lprc->right += infoPtr->iconSize.cx;
5723 if (infoPtr->himlSmall != NULL)
5724 lprc->right += infoPtr->iconSize.cx;
5726 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5727 nLabelWidth += TRAILING_PADDING;
5728 if (infoPtr->himlSmall)
5729 nLabelWidth += IMAGE_PADDING;
5730 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5731 lprc->right += nLabelWidth;
5732 else
5733 lprc->right = lprc->left + infoPtr->nItemWidth;
5736 else
5738 bResult = TRUE;
5739 lprc->left = ptItem.x;
5740 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5741 lprc->left += nIndent;
5742 lprc->right = lprc->left;
5743 lprc->top = ptItem.y;
5744 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5746 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5748 RECT br;
5749 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5750 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5752 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5754 else
5756 if (infoPtr->himlState != NULL)
5757 lprc->right += infoPtr->iconSize.cx;
5759 if (infoPtr->himlSmall != NULL)
5760 lprc->right += infoPtr->iconSize.cx;
5762 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5763 nLabelWidth += TRAILING_PADDING;
5764 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5765 lprc->right += nLabelWidth;
5766 else
5767 lprc->right = lprc->left + infoPtr->nItemWidth;
5770 break;
5772 case LVIR_SELECTBOUNDS:
5773 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptItem)) break;
5774 if (uView == LVS_ICON)
5776 if (infoPtr->himlNormal != NULL)
5778 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5780 bResult = TRUE;
5781 lprc->left = ptItem.x + ptOrigin.x;
5782 lprc->top = ptItem.y + ptOrigin.y;
5783 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5784 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5788 else if (uView == LVS_SMALLICON)
5790 if (LISTVIEW_GetOrigin(infoPtr, &ptOrigin))
5792 bResult = TRUE;
5793 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5794 lprc->top = ptItem.y + ptOrigin.y;
5795 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5797 if (infoPtr->himlState != NULL)
5798 lprc->left += infoPtr->iconSize.cx;
5800 lprc->right = lprc->left;
5802 if (infoPtr->himlSmall != NULL)
5803 lprc->right += infoPtr->iconSize.cx;
5805 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5806 nLabelWidth += TRAILING_PADDING;
5807 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5808 lprc->right += nLabelWidth;
5809 else
5810 lprc->right = nLeftPos + infoPtr->nItemWidth;
5813 else
5815 bResult = TRUE;
5816 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5817 nLeftPos = lprc->left = ptItem.x + nIndent;
5818 else
5819 nLeftPos = lprc->left = ptItem.x;
5820 lprc->top = ptItem.y;
5821 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5823 if (infoPtr->himlState != NULL)
5824 lprc->left += infoPtr->iconSize.cx;
5826 lprc->right = lprc->left;
5828 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5830 RECT br;
5831 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5832 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5834 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5836 else
5838 if (infoPtr->himlSmall != NULL)
5839 lprc->right += infoPtr->iconSize.cx;
5841 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
5842 nLabelWidth += TRAILING_PADDING;
5843 if (infoPtr->himlSmall)
5844 nLabelWidth += IMAGE_PADDING;
5845 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5846 lprc->right += nLabelWidth;
5847 else
5848 lprc->right = nLeftPos + infoPtr->nItemWidth;
5851 break;
5853 TRACE("result %s (%d,%d)-(%d,%d)\n",
5854 (bResult) ? "TRUE" : "FALSE",
5855 lprc->left, lprc->top, lprc->right, lprc->bottom);
5858 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5859 lprc->left, lprc->top, lprc->right, lprc->bottom);
5861 return bResult;
5865 static LRESULT LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem, INT
5866 flags, LPRECT lprc)
5868 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
5869 INT count;
5871 TRACE("(nItem=%d, nSubItem=%d lprc=%p)\n", nItem, nSubItem,
5872 lprc);
5874 if (!(uView & LVS_REPORT))
5875 return FALSE;
5877 if (flags & LVIR_ICON)
5879 FIXME("Unimplemented LVIR_ICON\n");
5880 return FALSE;
5882 else
5884 int top = min(infoPtr->nColumnCount, nSubItem - 1);
5886 LISTVIEW_GetItemRect(infoPtr,nItem,lprc);
5887 for (count = 0; count < top; count++)
5888 lprc->left += LISTVIEW_GetColumnWidth(infoPtr,count);
5890 lprc->right = LISTVIEW_GetColumnWidth(infoPtr,(nSubItem-1)) +
5891 lprc->left;
5893 return TRUE;
5897 /***
5898 * DESCRIPTION:
5899 * Retrieves the width of a label.
5901 * PARAMETER(S):
5902 * [I] infoPtr : valid pointer to the listview structure
5904 * RETURN:
5905 * SUCCESS : string width (in pixels)
5906 * FAILURE : zero
5908 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5910 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5911 INT nLabelWidth = 0;
5912 LVITEMW lvItem;
5914 TRACE("(nItem=%d)\n", nItem);
5916 ZeroMemory(&lvItem, sizeof(lvItem));
5917 lvItem.mask = LVIF_TEXT;
5918 lvItem.iItem = nItem;
5919 lvItem.cchTextMax = DISP_TEXT_SIZE;
5920 lvItem.pszText = szDispText;
5921 if (LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE))
5922 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5924 return nLabelWidth;
5927 /***
5928 * DESCRIPTION:
5929 * Retrieves the spacing between listview control items.
5931 * PARAMETER(S):
5932 * [I] infoPtr : valid pointer to the listview structure
5933 * [I] BOOL : flag for small or large icon
5935 * RETURN:
5936 * Horizontal + vertical spacing
5938 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5940 LONG lResult;
5942 if (!bSmall)
5944 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5946 else
5948 LONG style = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
5949 if ((style & LVS_TYPEMASK) == LVS_ICON)
5950 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5951 else
5952 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5954 return lResult;
5957 /***
5958 * DESCRIPTION:
5959 * Retrieves the state of a listview control item.
5961 * PARAMETER(S):
5962 * [I] infoPtr : valid pointer to the listview structure
5963 * [I] INT : item index
5964 * [I] UINT : state mask
5966 * RETURN:
5967 * State specified by the mask.
5969 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5971 LVITEMW lvItem;
5972 UINT uState = 0;
5974 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5976 ZeroMemory(&lvItem, sizeof(lvItem));
5977 lvItem.iItem = nItem;
5978 lvItem.stateMask = uMask;
5979 lvItem.mask = LVIF_STATE;
5980 if (LISTVIEW_GetItemW(infoPtr, &lvItem, TRUE))
5981 uState = lvItem.state;
5984 return uState;
5987 /***
5988 * DESCRIPTION:
5989 * Retrieves the text of a listview control item or subitem.
5991 * PARAMETER(S):
5992 * [I] hwnd : window handle
5993 * [I] nItem : item index
5994 * [IO] lpLVItem : item information
5995 * [I] isW : TRUE if lpLVItem is Unicode
5997 * RETURN:
5998 * SUCCESS : string length
5999 * FAILURE : 0
6001 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6003 INT nLength = 0;
6005 if (lpLVItem != NULL)
6007 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6009 lpLVItem->mask = LVIF_TEXT;
6010 lpLVItem->iItem = nItem;
6011 if (LISTVIEW_GetItemT(infoPtr, lpLVItem, FALSE, isW))
6012 nLength = textlenT(lpLVItem->pszText, isW);
6016 return nLength;
6019 /***
6020 * DESCRIPTION:
6021 * Searches for an item based on properties + relationships.
6023 * PARAMETER(S):
6024 * [I] infoPtr : valid pointer to the listview structure
6025 * [I] INT : item index
6026 * [I] INT : relationship flag
6028 * RETURN:
6029 * SUCCESS : item index
6030 * FAILURE : -1
6032 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6034 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
6035 UINT uMask = 0;
6036 LVFINDINFOW lvFindInfo;
6037 INT nCountPerColumn;
6038 INT i;
6040 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6042 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6044 if (uFlags & LVNI_CUT)
6045 uMask |= LVIS_CUT;
6047 if (uFlags & LVNI_DROPHILITED)
6048 uMask |= LVIS_DROPHILITED;
6050 if (uFlags & LVNI_FOCUSED)
6051 uMask |= LVIS_FOCUSED;
6053 if (uFlags & LVNI_SELECTED)
6054 uMask |= LVIS_SELECTED;
6056 if (uFlags & LVNI_ABOVE)
6058 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6060 while (nItem >= 0)
6062 nItem--;
6063 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6064 return nItem;
6067 else
6069 lvFindInfo.flags = LVFI_NEARESTXY;
6070 lvFindInfo.vkDirection = VK_UP;
6071 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
6072 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6074 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6075 return nItem;
6079 else if (uFlags & LVNI_BELOW)
6081 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6083 while (nItem < GETITEMCOUNT(infoPtr))
6085 nItem++;
6086 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6087 return nItem;
6090 else
6092 lvFindInfo.flags = LVFI_NEARESTXY;
6093 lvFindInfo.vkDirection = VK_DOWN;
6094 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
6095 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6097 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6098 return nItem;
6102 else if (uFlags & LVNI_TOLEFT)
6104 if (uView == LVS_LIST)
6106 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6107 while (nItem - nCountPerColumn >= 0)
6109 nItem -= nCountPerColumn;
6110 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6111 return nItem;
6114 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6116 lvFindInfo.flags = LVFI_NEARESTXY;
6117 lvFindInfo.vkDirection = VK_LEFT;
6118 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
6119 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6121 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6122 return nItem;
6126 else if (uFlags & LVNI_TORIGHT)
6128 if (uView == LVS_LIST)
6130 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6131 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6133 nItem += nCountPerColumn;
6134 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6135 return nItem;
6138 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6140 lvFindInfo.flags = LVFI_NEARESTXY;
6141 lvFindInfo.vkDirection = VK_RIGHT;
6142 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
6143 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6145 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6146 return nItem;
6150 else
6152 nItem++;
6154 /* search by index */
6155 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6157 if ((ListView_GetItemState(infoPtr->hwndSelf, i, uMask) & uMask) == uMask)
6158 return i;
6163 return -1;
6166 /* LISTVIEW_GetNumberOfWorkAreas */
6168 /***
6169 * DESCRIPTION:
6170 * Retrieves the origin coordinates when in icon or small icon display mode.
6172 * PARAMETER(S):
6173 * [I] infoPtr : valid pointer to the listview structure
6174 * [O] LPPOINT : coordinate information
6176 * RETURN:
6177 * SUCCESS : TRUE
6178 * FAILURE : FALSE
6180 static LRESULT LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6182 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
6183 UINT uView = lStyle & LVS_TYPEMASK;
6184 BOOL bResult = FALSE;
6186 TRACE("(lpptOrigin=%p)\n", lpptOrigin);
6188 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6190 SCROLLINFO scrollInfo;
6191 ZeroMemory(lpptOrigin, sizeof(POINT));
6192 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6193 scrollInfo.cbSize = sizeof(SCROLLINFO);
6195 if (lStyle & WS_HSCROLL)
6197 scrollInfo.fMask = SIF_POS;
6198 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6199 lpptOrigin->x = -scrollInfo.nPos;
6202 if (lStyle & WS_VSCROLL)
6204 scrollInfo.fMask = SIF_POS;
6205 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6206 lpptOrigin->y = -scrollInfo.nPos;
6209 bResult = TRUE;
6211 TRACE("(pt=(%ld,%ld))\n", lpptOrigin->x, lpptOrigin->y);
6215 return bResult;
6218 /***
6219 * DESCRIPTION:
6220 * Retrieves the number of items that are marked as selected.
6222 * PARAMETER(S):
6223 * [I] infoPtr : valid pointer to the listview structure
6225 * RETURN:
6226 * Number of items selected.
6228 static LRESULT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
6230 /* REDO THIS */
6231 INT nSelectedCount = 0;
6232 INT i;
6234 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6236 if (ListView_GetItemState(infoPtr->hwndSelf, i, LVIS_SELECTED) & LVIS_SELECTED)
6237 nSelectedCount++;
6240 return nSelectedCount;
6243 /***
6244 * DESCRIPTION:
6245 * Retrieves the width of a string.
6247 * PARAMETER(S):
6248 * [I] hwnd : window handle
6249 * [I] lpszText : text string to process
6250 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6252 * RETURN:
6253 * SUCCESS : string width (in pixels)
6254 * FAILURE : zero
6256 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6258 if (is_textT(lpszText, isW))
6260 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6261 HDC hdc = GetDC(infoPtr->hwndSelf);
6262 HFONT hOldFont = SelectObject(hdc, hFont);
6263 SIZE stringSize;
6264 ZeroMemory(&stringSize, sizeof(SIZE));
6265 if (isW)
6266 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6267 else
6268 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6269 SelectObject(hdc, hOldFont);
6270 ReleaseDC(infoPtr->hwndSelf, hdc);
6271 return stringSize.cx;
6273 return 0;
6276 /***
6277 * DESCRIPTION:
6278 * Retrieves the text backgound color.
6280 * PARAMETER(S):
6281 * [I] infoPtr : valid pointer to the listview structure
6283 * RETURN:
6284 * COLORREF associated with the the background.
6286 static LRESULT LISTVIEW_GetTextBkColor(LISTVIEW_INFO *infoPtr)
6288 return infoPtr->clrTextBk;
6291 /***
6292 * DESCRIPTION:
6293 * Retrieves the text color.
6295 * PARAMETER(S):
6296 * [I] infoPtr : valid pointer to the listview structure
6298 * RETURN:
6299 * COLORREF associated with the text.
6301 static LRESULT LISTVIEW_GetTextColor(LISTVIEW_INFO *infoPtr)
6303 return infoPtr->clrText;
6306 /***
6307 * DESCRIPTION:
6308 * Determines item if a hit or closest if not
6310 * PARAMETER(S):
6311 * [I] infoPtr : valid pointer to the listview structure
6312 * [IO] LPLV_INTHIT : hit test information
6313 * [I] subitem : fill out iSubItem.
6315 * RETURN:
6316 * SUCCESS : item index of hit
6317 * FAILURE : -1
6319 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *infoPtr, LPLV_INTHIT lpInt, BOOL subitem)
6321 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
6322 UINT uView = lStyle & LVS_TYPEMASK;
6323 INT i,j,topindex,bottomindex;
6324 RECT rcItem,rcSubItem;
6325 DWORD xterm, yterm, dist;
6327 TRACE("(x=%ld, y=%ld)\n", lpInt->ht.pt.x, lpInt->ht.pt.y);
6329 topindex = LISTVIEW_GetTopIndex(infoPtr);
6330 if (uView == LVS_REPORT)
6332 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6333 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6335 else
6337 bottomindex = GETITEMCOUNT(infoPtr);
6340 lpInt->distance = 0x7fffffff;
6341 lpInt->iDistItem = -1;
6343 for (i = topindex; i < bottomindex; i++)
6345 rcItem.left = LVIR_BOUNDS;
6346 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
6348 if (PtInRect(&rcItem, lpInt->ht.pt))
6350 rcSubItem = rcItem;
6351 rcItem.left = LVIR_ICON;
6352 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
6354 if (PtInRect(&rcItem, lpInt->ht.pt))
6356 lpInt->ht.flags = LVHT_ONITEMICON;
6357 lpInt->ht.iItem = i;
6358 goto set_subitem;
6362 rcItem.left = LVIR_LABEL;
6363 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
6365 if (PtInRect(&rcItem, lpInt->ht.pt))
6367 lpInt->ht.flags = LVHT_ONITEMLABEL;
6368 lpInt->ht.iItem = i;
6369 goto set_subitem;
6373 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6374 lpInt->ht.iItem = i;
6375 set_subitem:
6376 if (subitem)
6378 lpInt->ht.iSubItem = 0;
6379 rcSubItem.right = rcSubItem.left;
6380 for (j = 0; j < infoPtr->nColumnCount; j++)
6382 rcSubItem.left = rcSubItem.right;
6383 rcSubItem.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6384 if (PtInRect(&rcSubItem, lpInt->ht.pt))
6386 lpInt->ht.iSubItem = j;
6387 break;
6391 return i;
6393 else
6396 * Now compute distance from point to center of boundary
6397 * box. Since we are only interested in the relative
6398 * distance, we can skip the nasty square root operation
6400 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6401 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6402 dist = xterm * xterm + yterm * yterm;
6403 if (dist < lpInt->distance)
6405 lpInt->distance = dist;
6406 lpInt->iDistItem = i;
6412 lpInt->ht.flags = LVHT_NOWHERE;
6413 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6415 return -1;
6418 /***
6419 * DESCRIPTION:
6420 * Determines which section of the item was selected (if any).
6422 * PARAMETER(S):
6423 * [I] infoPtr : valid pointer to the listview structure
6424 * [IO] LPLVHITTESTINFO : hit test information
6425 * [I] subitem : fill out iSubItem.
6427 * RETURN:
6428 * SUCCESS : item index
6429 * FAILURE : -1
6431 static INT LISTVIEW_HitTestItem(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6433 INT ret;
6434 LV_INTHIT lv_inthit;
6436 TRACE("(x=%ld, y=%ld)\n", lpHitTestInfo->pt.x,
6437 lpHitTestInfo->pt.y);
6439 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6440 ret = LISTVIEW_SuperHitTestItem(infoPtr, &lv_inthit, subitem);
6441 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6442 return ret;
6445 /***
6446 * DESCRIPTION:
6447 * Determines which listview item is located at the specified position.
6449 * PARAMETER(S):
6450 * [I] infoPtr : valid pointer to the listview structure
6451 * [IO} LPLVHITTESTINFO : hit test information
6453 * RETURN:
6454 * SUCCESS : item index
6455 * FAILURE : -1
6457 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpHitTestInfo)
6459 INT nItem = -1;
6461 lpHitTestInfo->flags = 0;
6463 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6464 lpHitTestInfo->flags = LVHT_TOLEFT;
6465 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6466 lpHitTestInfo->flags = LVHT_TORIGHT;
6467 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6468 lpHitTestInfo->flags |= LVHT_ABOVE;
6469 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6470 lpHitTestInfo->flags |= LVHT_BELOW;
6472 if (lpHitTestInfo->flags == 0)
6474 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6475 * an app might pass only a structure with space up to iItem!
6476 * (MS Office 97 does that for instance in the file open dialog)
6478 nItem = LISTVIEW_HitTestItem(infoPtr, lpHitTestInfo, FALSE);
6481 return nItem;
6484 /***
6485 * DESCRIPTION:
6486 * Determines which listview subitem is located at the specified position.
6488 * PARAMETER(S):
6489 * [I] infoPtr : valid pointer to the listview structure
6490 * [IO} LPLVHITTESTINFO : hit test information
6492 * RETURN:
6493 * SUCCESS : item index
6494 * FAILURE : -1
6496 static LRESULT LISTVIEW_SubItemHitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpHitTestInfo)
6498 INT nItem = -1;
6500 lpHitTestInfo->flags = 0;
6502 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6503 lpHitTestInfo->flags = LVHT_TOLEFT;
6504 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6505 lpHitTestInfo->flags = LVHT_TORIGHT;
6506 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6507 lpHitTestInfo->flags |= LVHT_ABOVE;
6508 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6509 lpHitTestInfo->flags |= LVHT_BELOW;
6511 if (lpHitTestInfo->flags == 0)
6512 nItem = LISTVIEW_HitTestItem(infoPtr, lpHitTestInfo, TRUE);
6514 return nItem;
6517 /***
6518 * DESCRIPTION:
6519 * Inserts a new column.
6521 * PARAMETER(S):
6522 * [I] infoPtr : valid pointer to the listview structure
6523 * [I] INT : column index
6524 * [I] LPLVCOLUMNW : column information
6526 * RETURN:
6527 * SUCCESS : new column index
6528 * FAILURE : -1
6530 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6531 LPLVCOLUMNW lpColumn, BOOL isW)
6533 INT nNewColumn = -1;
6534 HDITEMW hdi;
6536 TRACE("(nColumn=%d, lpColumn=%p)\n", nColumn, lpColumn);
6538 if (lpColumn != NULL)
6540 /* initialize memory */
6541 ZeroMemory(&hdi, sizeof(hdi));
6543 if (lpColumn->mask & LVCF_FMT)
6545 /* format member is valid */
6546 hdi.mask |= HDI_FORMAT;
6548 /* set text alignment (leftmost column must be left-aligned) */
6549 if (nColumn == 0)
6551 hdi.fmt |= HDF_LEFT;
6553 else
6555 if (lpColumn->fmt & LVCFMT_LEFT)
6557 hdi.fmt |= HDF_LEFT;
6559 else if (lpColumn->fmt & LVCFMT_RIGHT)
6561 hdi.fmt |= HDF_RIGHT;
6563 else if (lpColumn->fmt & LVCFMT_CENTER)
6565 hdi.fmt |= HDF_CENTER;
6569 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6571 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6572 /* ??? */
6575 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6577 /* ??? */
6580 if (lpColumn->fmt & LVCFMT_IMAGE)
6582 hdi.fmt |= HDF_IMAGE;
6583 hdi.iImage = I_IMAGECALLBACK;
6587 if (lpColumn->mask & LVCF_WIDTH)
6589 hdi.mask |= HDI_WIDTH;
6590 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6592 /* make it fill the remainder of the controls width */
6593 HDITEMW hdit;
6594 RECT rcHeader;
6595 INT item_index;
6597 ZeroMemory(&hdit, sizeof(hdit));
6599 /* get the width of every item except the current one */
6600 hdit.mask = HDI_WIDTH;
6601 hdi.cxy = 0;
6603 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6604 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6605 hdi.cxy+=hdit.cxy;
6608 /* retrieve the layout of the header */
6609 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6610 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6611 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6613 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6615 else
6616 hdi.cxy = lpColumn->cx;
6619 if (lpColumn->mask & LVCF_TEXT)
6621 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6622 hdi.pszText = lpColumn->pszText;
6623 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6624 hdi.fmt |= HDF_STRING;
6627 if (lpColumn->mask & LVCF_IMAGE)
6629 hdi.mask |= HDI_IMAGE;
6630 hdi.iImage = lpColumn->iImage;
6633 if (lpColumn->mask & LVCF_ORDER)
6635 hdi.mask |= HDI_ORDER;
6636 hdi.iOrder = lpColumn->iOrder;
6639 /* insert item in header control */
6640 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6641 (WPARAM)nColumn, (LPARAM)&hdi);
6643 /* Need to reset the item width when inserting a new column */
6644 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
6646 LISTVIEW_UpdateScroll(infoPtr);
6647 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
6650 return nNewColumn;
6653 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6654 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6655 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6656 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6657 their own sort proc. when sending LVM_SORTITEMS.
6659 /* Platform SDK:
6660 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6662 LVS_SORTXXX must be specified,
6663 LVS_OWNERDRAW is not set,
6664 <item>.pszText is not LPSTR_TEXTCALLBACK.
6666 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6667 are sorted based on item text..."
6669 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6671 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6672 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6673 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6674 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6675 /* if we're sorting descending, negate the return value */
6676 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6679 /***
6680 * nESCRIPTION:
6681 * Inserts a new item in the listview control.
6683 * PARAMETER(S):
6684 * [I] infoPtr : valid pointer to the listview structure
6685 * [I] LPLVITEMW : item information
6686 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6688 * RETURN:
6689 * SUCCESS : new item index
6690 * FAILURE : -1
6692 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6694 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
6695 UINT uView = lStyle & LVS_TYPEMASK;
6696 INT nItem = -1;
6697 HDPA hdpaSubItems;
6698 INT nItemWidth = 0;
6699 LISTVIEW_ITEM *lpItem = NULL;
6701 TRACE("(lpLVItem=%s, isW=%d)\n",
6702 debuglvitem_t(lpLVItem, isW), isW);
6704 if (lStyle & LVS_OWNERDATA)
6706 nItem = infoPtr->hdpaItems->nItemCount;
6707 infoPtr->hdpaItems->nItemCount ++;
6708 return nItem;
6711 if (lpLVItem != NULL)
6713 /* make sure it's not a subitem; cannot insert a subitem */
6714 if (lpLVItem->iSubItem == 0)
6716 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6718 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6719 if (LISTVIEW_InitItemT(infoPtr, lpItem, lpLVItem, isW))
6721 /* insert item in listview control data structure */
6722 if ( (hdpaSubItems = DPA_Create(8)) )
6724 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6726 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6727 && !(lStyle & LVS_OWNERDRAWFIXED)
6728 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6730 /* Insert the item in the proper sort order based on the pszText
6731 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6732 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6733 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6734 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr->hwndSelf );
6735 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6737 else
6739 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6740 hdpaSubItems);
6742 if (nItem != -1)
6744 NMLISTVIEW nmlv;
6746 LISTVIEW_ShiftIndices(infoPtr,nItem,1);
6748 /* manage item focus */
6749 if (lpLVItem->mask & LVIF_STATE)
6751 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6752 if (lpLVItem->stateMask & LVIS_SELECTED)
6753 LISTVIEW_SetSelection(infoPtr, nItem);
6754 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6755 LISTVIEW_SetItemFocus(infoPtr, nItem);
6758 /* send LVN_INSERTITEM notification */
6759 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6760 nmlv.iItem = nItem;
6761 nmlv.lParam = lpItem->lParam;
6762 listview_notify(infoPtr, LVN_INSERTITEM, &nmlv);
6764 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6766 nItemWidth = LISTVIEW_CalculateWidth(infoPtr, lpLVItem->iItem);
6767 if (nItemWidth > infoPtr->nItemWidth)
6768 infoPtr->nItemWidth = nItemWidth;
6771 /* align items (set position of each item) */
6772 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6774 if (lStyle & LVS_ALIGNLEFT)
6775 LISTVIEW_AlignLeft(infoPtr);
6776 else
6777 LISTVIEW_AlignTop(infoPtr);
6780 LISTVIEW_UpdateScroll(infoPtr);
6781 /* refresh client area */
6782 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
6791 /* free memory if unsuccessful */
6792 if ((nItem == -1) && (lpItem != NULL))
6793 COMCTL32_Free(lpItem);
6795 return nItem;
6798 /***
6799 * DESCRIPTION:
6800 * Redraws a range of items.
6802 * PARAMETER(S):
6803 * [I] infoPtr : valid pointer to the listview structure
6804 * [I] INT : first item
6805 * [I] INT : last item
6807 * RETURN:
6808 * SUCCESS : TRUE
6809 * FAILURE : FALSE
6811 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6813 BOOL bResult = FALSE;
6814 RECT rcItem;
6815 INT i;
6817 if (nFirst <= nLast)
6819 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6821 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6823 for (i = nFirst; i <= nLast; i++)
6825 rcItem.left = LVIR_BOUNDS;
6826 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
6827 InvalidateRect(infoPtr->hwndSelf, &rcItem, TRUE);
6833 return bResult;
6836 /***
6837 * DESCRIPTION:
6838 * Scroll the content of a listview.
6840 * PARAMETER(S):
6841 * [I] infoPtr : valid pointer to the listview structure
6842 * [I] INT : horizontal scroll amount in pixels
6843 * [I] INT : vertical scroll amount in pixels
6845 * RETURN:
6846 * SUCCESS : TRUE
6847 * FAILURE : FALSE
6849 * COMMENTS:
6850 * If the control is in report mode (LVS_REPORT) the control can
6851 * be scrolled only in line increments. "dy" will be rounded to the
6852 * nearest number of pixels that are a whole line. Ex: if line height
6853 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6854 * is passed the the scroll will be 0. (per MSDN 7/2002)
6856 * For: (per experimentaion with native control and CSpy ListView)
6857 * LVS_ICON dy=1 = 1 pixel (vertical only)
6858 * dx ignored
6859 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6860 * dx ignored
6861 * LVS_LIST dx=1 = 1 column (horizontal only)
6862 * but will only scroll 1 column per message
6863 * no matter what the value.
6864 * dy must be 0 or FALSE returned.
6865 * LVS_REPORT dx=1 = 1 pixel
6866 * dy= see above
6869 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6871 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
6872 UINT uView = lStyle & LVS_TYPEMASK;
6873 INT rows, mode, i;
6875 if (uView == LVS_REPORT)
6877 rows = (abs(dy) + infoPtr->nItemHeight/2) / infoPtr->nItemHeight;
6878 if (rows != 0)
6880 mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
6881 for ( i=0; i<rows; i++)
6882 LISTVIEW_VScroll(infoPtr, mode, 0, 0);
6885 if (dx != 0)
6887 mode = (dx>0) ? SB_INTERNAL_RIGHT : SB_INTERNAL_LEFT;
6888 for ( i=0; i<abs(dx); i++)
6889 LISTVIEW_HScroll(infoPtr, mode, 0, 0);
6891 return TRUE;
6893 else if (uView == LVS_ICON)
6895 INT mode, i;
6897 mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
6898 for(i=0; i<abs(dy); i++)
6899 LISTVIEW_VScroll(infoPtr, mode, 0, 0);
6900 return TRUE;
6902 else if (uView == LVS_SMALLICON)
6904 INT mode, i;
6906 mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
6907 for(i=0; i<abs(dy); i++)
6908 LISTVIEW_VScroll(infoPtr, mode, 0, 0);
6909 return TRUE;
6911 else if (uView == LVS_LIST)
6913 if (dy != 0) return FALSE;
6914 if (dx == 0) return TRUE;
6915 mode = (dx>0) ? SB_INTERNAL_RIGHT : SB_INTERNAL_LEFT;
6916 LISTVIEW_HScroll(infoPtr, mode, 0, 0);
6917 return TRUE;
6919 return FALSE;
6922 /***
6923 * DESCRIPTION:
6924 * Sets the background color.
6926 * PARAMETER(S):
6927 * [I] infoPtr : valid pointer to the listview structure
6928 * [I] COLORREF : background color
6930 * RETURN:
6931 * SUCCESS : TRUE
6932 * FAILURE : FALSE
6934 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6936 if(infoPtr->clrBk!=clrBk){
6937 infoPtr->clrBk = clrBk;
6938 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
6941 return TRUE;
6944 /* LISTVIEW_SetBkImage */
6946 /***
6947 * DESCRIPTION:
6948 * Sets the callback mask. This mask will be used when the parent
6949 * window stores state information (some or all).
6951 * PARAMETER(S):
6952 * [I] infoPtr : valid pointer to the listview structure
6953 * [I] UINT : state mask
6955 * RETURN:
6956 * SUCCESS : TRUE
6957 * FAILURE : FALSE
6959 static BOOL LISTVIEW_SetCallbackMask(LISTVIEW_INFO *infoPtr, UINT uMask)
6961 infoPtr->uCallbackMask = uMask;
6963 return TRUE;
6966 /***
6967 * DESCRIPTION:
6968 * Sets the attributes of a header item.
6970 * PARAMETER(S):
6971 * [I] infoPtr : valid pointer to the listview structure
6972 * [I] INT : column index
6973 * [I] LPLVCOLUMNW : column attributes
6974 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6975 * otherwise it is in fact a LPLVCOLUMNA
6977 * RETURN:
6978 * SUCCESS : TRUE
6979 * FAILURE : FALSE
6981 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6982 LPLVCOLUMNW lpColumn, BOOL isW)
6984 BOOL bResult = FALSE;
6985 HDITEMW hdi, hdiget;
6987 if ((lpColumn != NULL) && (nColumn >= 0) &&
6988 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6990 /* initialize memory */
6991 ZeroMemory(&hdi, sizeof(hdi));
6993 if (lpColumn->mask & LVCF_FMT)
6995 /* format member is valid */
6996 hdi.mask |= HDI_FORMAT;
6998 /* get current format first */
6999 hdiget.mask = HDI_FORMAT;
7000 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7001 /* preserve HDF_STRING if present */
7002 hdi.fmt = hdiget.fmt & HDF_STRING;
7004 /* set text alignment (leftmost column must be left-aligned) */
7005 if (nColumn == 0)
7007 hdi.fmt |= HDF_LEFT;
7009 else
7011 if (lpColumn->fmt & LVCFMT_LEFT)
7012 hdi.fmt |= HDF_LEFT;
7013 else if (lpColumn->fmt & LVCFMT_RIGHT)
7014 hdi.fmt |= HDF_RIGHT;
7015 else if (lpColumn->fmt & LVCFMT_CENTER)
7016 hdi.fmt |= HDF_CENTER;
7019 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7020 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7022 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7023 hdi.fmt |= HDF_IMAGE;
7025 if (lpColumn->fmt & LVCFMT_IMAGE)
7027 hdi.fmt |= HDF_IMAGE;
7028 hdi.iImage = I_IMAGECALLBACK;
7032 if (lpColumn->mask & LVCF_WIDTH)
7034 hdi.mask |= HDI_WIDTH;
7035 hdi.cxy = lpColumn->cx;
7038 if (lpColumn->mask & LVCF_TEXT)
7040 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7041 hdi.pszText = lpColumn->pszText;
7042 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7043 hdi.fmt |= HDF_STRING;
7046 if (lpColumn->mask & LVCF_IMAGE)
7048 hdi.mask |= HDI_IMAGE;
7049 hdi.iImage = lpColumn->iImage;
7052 if (lpColumn->mask & LVCF_ORDER)
7054 hdi.mask |= HDI_ORDER;
7055 hdi.iOrder = lpColumn->iOrder;
7058 /* set header item attributes */
7059 if (isW)
7060 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7061 else
7062 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
7065 return bResult;
7068 /***
7069 * DESCRIPTION:
7070 * Sets the column order array
7072 * PARAMETERS:
7073 * [I] infoPtr : valid pointer to the listview structure
7074 * [I] INT : number of elements in column order array
7075 * [I] INT : pointer to column order array
7077 * RETURN:
7078 * SUCCESS : TRUE
7079 * FAILURE : FALSE
7081 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
7083 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7085 if (!lpiArray)
7086 return FALSE;
7088 return TRUE;
7092 /***
7093 * DESCRIPTION:
7094 * Sets the width of a column
7096 * PARAMETERS:
7097 * [I] infoPtr : valid pointer to the listview structure
7098 * [I] INT : column index
7099 * [I] INT : column width
7101 * RETURN:
7102 * SUCCESS : TRUE
7103 * FAILURE : FALSE
7105 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
7107 HDITEMW hdi;
7108 LRESULT lret;
7109 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7110 UINT uView = lStyle & LVS_TYPEMASK;
7111 HDC hdc;
7112 HFONT header_font;
7113 HFONT old_font;
7114 SIZE size;
7115 WCHAR text_buffer[DISP_TEXT_SIZE];
7116 INT header_item_count;
7117 INT item_index;
7118 INT nLabelWidth;
7119 RECT rcHeader;
7120 LVITEMW lvItem;
7121 WCHAR szDispText[DISP_TEXT_SIZE];
7123 if (!infoPtr->hwndHeader) /* make sure we have a header */
7124 return (FALSE);
7126 /* set column width only if in report or list mode */
7127 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7128 return (FALSE);
7130 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
7132 /* take care of invalid cx values */
7133 if((uView == LVS_REPORT) && (cx < -2))
7134 cx = LVSCW_AUTOSIZE;
7135 else if (uView == LVS_LIST && (cx < 1))
7136 return FALSE;
7138 /* resize all columns if in LVS_LIST mode */
7139 if(uView == LVS_LIST) {
7140 infoPtr->nItemWidth = cx;
7141 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); /* force redraw of the listview */
7142 return TRUE;
7145 /* autosize based on listview items width */
7146 if(cx == LVSCW_AUTOSIZE)
7148 /* set the width of the column to the width of the widest item */
7149 if (iCol == 0 || uView == LVS_LIST)
7151 cx = 0;
7152 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7154 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
7155 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7157 if (infoPtr->himlSmall)
7158 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7160 else
7162 ZeroMemory(&lvItem, sizeof(lvItem));
7163 lvItem.iSubItem = iCol;
7164 lvItem.mask = LVIF_TEXT;
7165 lvItem.cchTextMax = DISP_TEXT_SIZE;
7166 lvItem.pszText = szDispText;
7167 *lvItem.pszText = '\0';
7168 cx = 0;
7169 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7171 lvItem.iItem = item_index;
7172 LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, TRUE);
7173 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7174 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7177 cx += TRAILING_PADDING;
7178 } /* autosize based on listview header width */
7179 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7181 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7183 /* if iCol is the last column make it fill the remainder of the controls width */
7184 if(iCol == (header_item_count - 1)) {
7185 /* get the width of every item except the current one */
7186 hdi.mask = HDI_WIDTH;
7187 cx = 0;
7189 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7190 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7191 cx+=hdi.cxy;
7194 /* retrieve the layout of the header */
7195 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7197 cx = (rcHeader.right - rcHeader.left) - cx;
7199 else
7201 /* Despite what the MS docs say, if this is not the last
7202 column, then MS resizes the column to the width of the
7203 largest text string in the column, including headers
7204 and items. This is different from LVSCW_AUTOSIZE in that
7205 LVSCW_AUTOSIZE ignores the header string length.
7208 /* retrieve header font */
7209 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7211 /* retrieve header text */
7212 hdi.mask = HDI_TEXT;
7213 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7214 hdi.pszText = text_buffer;
7216 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7218 /* determine the width of the text in the header */
7219 hdc = GetDC(infoPtr->hwndSelf);
7220 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7222 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7224 SelectObject(hdc, old_font); /* restore the old font */
7225 ReleaseDC(infoPtr->hwndSelf, hdc);
7227 ZeroMemory(&lvItem, sizeof(lvItem));
7228 lvItem.iSubItem = iCol;
7229 lvItem.mask = LVIF_TEXT;
7230 lvItem.cchTextMax = DISP_TEXT_SIZE;
7231 lvItem.pszText = szDispText;
7232 *lvItem.pszText = '\0';
7233 cx = size.cx;
7234 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7236 lvItem.iItem = item_index;
7237 LISTVIEW_GetItemT(infoPtr, &lvItem, FALSE, TRUE);
7238 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7239 nLabelWidth += TRAILING_PADDING;
7240 /* While it is possible for subitems to have icons, even MS messes
7241 up the positioning, so I suspect no applications actually use
7242 them. */
7243 if (item_index == 0 && infoPtr->himlSmall)
7244 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7245 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7250 /* call header to update the column change */
7251 hdi.mask = HDI_WIDTH;
7253 hdi.cxy = cx;
7254 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7256 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); /* force redraw of the listview */
7258 return lret;
7261 /***
7262 * DESCRIPTION:
7263 * Sets the extended listview style.
7265 * PARAMETERS:
7266 * [I] infoPtr : valid pointer to the listview structure
7267 * [I] DWORD : mask
7268 * [I] DWORD : style
7270 * RETURN:
7271 * SUCCESS : previous style
7272 * FAILURE : 0
7274 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
7276 DWORD dwOldStyle = infoPtr->dwExStyle;
7278 /* set new style */
7279 if (dwMask)
7280 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7281 else
7282 infoPtr->dwExStyle = dwStyle;
7284 return dwOldStyle;
7287 /***
7288 * DESCRIPTION:
7289 * Sets the new hot cursor used during hot tracking and hover selection.
7291 * PARAMETER(S):
7292 * [I] infoPtr : valid pointer to the listview structure
7293 * [I} hCurosr : the new hot cursor handle
7295 * RETURN:
7296 * Returns the previous hot cursor
7298 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7300 HCURSOR oldCursor = infoPtr->hHotCursor;
7301 infoPtr->hHotCursor = hCursor;
7302 return oldCursor;
7306 /***
7307 * DESCRIPTION:
7308 * Sets the hot item index.
7310 * PARAMETERS:
7311 * [I] infoPtr : valid pointer to the listview structure
7312 * [I] INT : index
7314 * RETURN:
7315 * SUCCESS : previous hot item index
7316 * FAILURE : -1 (no hot item)
7318 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7320 INT iOldIndex = infoPtr->nHotItem;
7322 /* set new style */
7323 infoPtr->nHotItem = iIndex;
7325 return iOldIndex;
7329 /***
7330 * DESCRIPTION:
7331 * Sets the amount of time the cursor must hover over an item before it is selected.
7333 * PARAMETER(S):
7334 * [I] infoPtr : valid pointer to the listview structure
7335 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7337 * RETURN:
7338 * Returns the previous hover time
7340 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7342 DWORD oldHoverTime = infoPtr->dwHoverTime;
7344 infoPtr->dwHoverTime = dwHoverTime;
7346 return oldHoverTime;
7349 /***
7350 * DESCRIPTION:
7351 * Sets spacing for icons of LVS_ICON style.
7353 * PARAMETER(S):
7354 * [I] infoPtr : valid pointer to the listview structure
7355 * [I] DWORD : MAKELONG(cx, cy)
7357 * RETURN:
7358 * MAKELONG(oldcx, oldcy)
7360 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
7362 INT cy = HIWORD(spacing);
7363 INT cx = LOWORD(spacing);
7364 DWORD oldspacing;
7365 LONG lStyle = GetWindowLongA(infoPtr->hwndSelf, GWL_STYLE);
7366 UINT uView = lStyle & LVS_TYPEMASK;
7368 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7369 if (cx == -1) /* set to default */
7370 cx = GetSystemMetrics(SM_CXICONSPACING);
7371 if (cy == -1) /* set to default */
7372 cy = GetSystemMetrics(SM_CYICONSPACING);
7374 if (cx)
7375 infoPtr->iconSpacing.cx = cx;
7376 else
7377 { /* if 0 then compute width */
7378 if (uView == LVS_ICON)
7379 FIXME("width computation not yet done\n");
7381 * Should scan each item and determine max width of
7382 * icon or label, then make that the width
7384 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7385 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(infoPtr);
7387 if (cy)
7388 infoPtr->iconSpacing.cy = cy;
7389 else
7390 { /* if 0 then compute height */
7391 if (uView == LVS_ICON)
7392 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight
7393 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7394 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7395 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7396 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(infoPtr);
7399 TRACE("old=(%d,%d), new=(%ld,%ld), iconSize=(%ld,%ld), ntmH=%d\n",
7400 LOWORD(oldspacing), HIWORD(oldspacing),
7401 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy,
7402 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7403 infoPtr->ntmHeight);
7405 /* these depend on the iconSpacing */
7406 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
7407 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7409 return oldspacing;
7412 /***
7413 * DESCRIPTION:
7414 * Sets image lists.
7416 * PARAMETER(S):
7417 * [I] infoPtr : valid pointer to the listview structure
7418 * [I] INT : image list type
7419 * [I] HIMAGELIST : image list handle
7421 * RETURN:
7422 * SUCCESS : old image list
7423 * FAILURE : NULL
7425 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7427 HIMAGELIST himlOld = 0;
7428 INT oldHeight;
7429 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
7431 switch (nType)
7433 case LVSIL_NORMAL:
7434 himlOld = infoPtr->himlNormal;
7435 infoPtr->himlNormal = himl;
7436 if(himl && (LVS_ICON == uView))
7438 INT cx, cy;
7439 ImageList_GetIconSize(himl, &cx, &cy);
7440 TRACE("icon old size=(%ld,%ld), new size=(%d,%d)\n",
7441 infoPtr->iconSize.cx, infoPtr->iconSize.cy, cx, cy);
7442 infoPtr->iconSize.cx = cx;
7443 infoPtr->iconSize.cy = cy;
7444 LISTVIEW_SetIconSpacing(infoPtr,0);
7446 break;
7448 case LVSIL_SMALL:
7449 himlOld = infoPtr->himlSmall;
7450 infoPtr->himlSmall = himl;
7451 break;
7453 case LVSIL_STATE:
7454 himlOld = infoPtr->himlState;
7455 infoPtr->himlState = himl;
7456 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7457 break;
7460 oldHeight = infoPtr->nItemHeight;
7461 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7462 if (infoPtr->nItemHeight != oldHeight)
7463 LISTVIEW_UpdateScroll(infoPtr);
7465 return himlOld;
7468 /***
7469 * DESCRIPTION:
7470 * Preallocates memory (does *not* set the actual count of items !)
7472 * PARAMETER(S):
7473 * [I] infoPtr : valid pointer to the listview structure
7474 * [I] INT : item count (projected number of items to allocate)
7475 * [I] DWORD : update flags
7477 * RETURN:
7478 * SUCCESS : TRUE
7479 * FAILURE : FALSE
7481 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7483 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7485 if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_OWNERDATA)
7487 int precount,topvisible;
7489 TRACE("LVS_OWNERDATA is set!\n");
7490 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7491 FIXME("flags %s %s not implemented\n",
7492 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7493 : "",
7494 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7497 * Internally remove all the selections.
7501 LISTVIEW_SELECTION *selection;
7502 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7503 if (selection)
7504 LISTVIEW_RemoveSelectionRange(infoPtr,selection->lower,
7505 selection->upper);
7507 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7509 precount = infoPtr->hdpaItems->nItemCount;
7510 topvisible = ListView_GetTopIndex(infoPtr->hwndSelf) +
7511 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
7513 infoPtr->hdpaItems->nItemCount = nItems;
7515 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(infoPtr),
7516 DEFAULT_COLUMN_WIDTH);
7518 LISTVIEW_UpdateSize(infoPtr);
7519 LISTVIEW_UpdateScroll(infoPtr);
7521 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7522 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7524 else
7526 /* According to MSDN for non-LVS_OWNERDATA this is just
7527 * a performance issue. The control allocates its internal
7528 * data structures for the number of items specified. It
7529 * cuts down on the number of memory allocations. Therefore
7530 * we will just issue a WARN here
7532 WARN("for non-ownerdata performance option not implemented.\n");
7535 return TRUE;
7538 /***
7539 * DESCRIPTION:
7540 * Sets the position of an item.
7542 * PARAMETER(S):
7543 * [I] infoPtr : valid pointer to the listview structure
7544 * [I] INT : item index
7545 * [I] LONG : x coordinate
7546 * [I] LONG : y coordinate
7548 * RETURN:
7549 * SUCCESS : TRUE
7550 * FAILURE : FALSE
7552 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem,
7553 LONG nPosX, LONG nPosY)
7555 UINT lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7556 UINT uView = lStyle & LVS_TYPEMASK;
7557 LISTVIEW_ITEM *lpItem;
7558 HDPA hdpaSubItems;
7559 BOOL bResult = FALSE;
7561 TRACE("(nItem=%d, X=%ld, Y=%ld)\n", nItem, nPosX, nPosY);
7563 if (lStyle & LVS_OWNERDATA)
7564 return FALSE;
7566 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7568 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7570 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7572 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7574 POINT orig;
7575 bResult = TRUE;
7576 orig = lpItem->ptPosition;
7577 if ((nPosX == -1) && (nPosY == -1))
7579 /* This point value seems to be an undocumented feature. The
7580 * best guess is that it means either at the origin, or at
7581 * the true beginning of the list. I will assume the origin.
7583 POINT pt1;
7584 if (!LISTVIEW_GetOrigin(infoPtr, &pt1))
7586 pt1.x = 0;
7587 pt1.y = 0;
7589 nPosX = pt1.x;
7590 nPosY = pt1.y;
7591 if (uView == LVS_ICON)
7593 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7594 nPosY += ICON_TOP_PADDING;
7596 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7597 nPosX, nPosY);
7600 lpItem->ptPosition.x = nPosX;
7601 lpItem->ptPosition.y = nPosY;
7602 if (uView == LVS_ICON)
7604 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7605 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7606 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7608 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7609 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7612 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7613 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7616 else
7618 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7619 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7627 return bResult;
7630 /***
7631 * DESCRIPTION:
7632 * Sets the state of one or many items.
7634 * PARAMETER(S):
7635 * [I] infoPtr : valid pointer to the listview structure
7636 * [I]INT : item index
7637 * [I] LPLVITEM : item or subitem info
7639 * RETURN:
7640 * SUCCESS : TRUE
7641 * FAILURE : FALSE
7643 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
7645 BOOL bResult = TRUE;
7646 LVITEMW lvItem;
7648 TRACE("(nItem=%d, lpLVItem=%s)\n",
7649 nItem, debuglvitem_t(lpLVItem, TRUE));
7651 ZeroMemory(&lvItem, sizeof(lvItem));
7652 lvItem.mask = LVIF_STATE;
7653 lvItem.state = lpLVItem->state;
7654 lvItem.stateMask = lpLVItem->stateMask ;
7655 lvItem.iItem = nItem;
7657 if (nItem == -1)
7659 /* apply to all items */
7660 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7661 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7663 else
7664 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7666 return bResult;
7669 /***
7670 * DESCRIPTION:
7671 * Sets the text of an item or subitem.
7673 * PARAMETER(S):
7674 * [I] hwnd : window handle
7675 * [I] nItem : item index
7676 * [I] lpLVItem : item or subitem info
7677 * [I] isW : TRUE if input is Unicode
7679 * RETURN:
7680 * SUCCESS : TRUE
7681 * FAILURE : FALSE
7683 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7685 BOOL bResult = FALSE;
7686 LVITEMW lvItem;
7688 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n",
7689 nItem, debuglvitem_t(lpLVItem, isW), isW);
7691 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7693 ZeroMemory(&lvItem, sizeof(LVITEMW));
7694 lvItem.mask = LVIF_TEXT;
7695 lvItem.pszText = lpLVItem->pszText;
7696 lvItem.iItem = nItem;
7697 lvItem.iSubItem = lpLVItem->iSubItem;
7698 if(isW) bResult = ListView_SetItemW(infoPtr->hwndSelf, &lvItem);
7699 else bResult = ListView_SetItemA(infoPtr->hwndSelf, &lvItem);
7702 return bResult;
7705 /***
7706 * DESCRIPTION:
7707 * Set item index that marks the start of a multiple selection.
7709 * PARAMETER(S):
7710 * [I] infoPtr : valid pointer to the listview structure
7711 * [I] INT : index
7713 * RETURN:
7714 * Index number or -1 if there is no selection mark.
7716 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7718 INT nOldIndex = infoPtr->nSelectionMark;
7720 TRACE("(nIndex=%d)\n", nIndex);
7722 infoPtr->nSelectionMark = nIndex;
7724 return nOldIndex;
7727 /***
7728 * DESCRIPTION:
7729 * Sets the text background color.
7731 * PARAMETER(S):
7732 * [I] infoPtr : valid pointer to the listview structure
7733 * [I] COLORREF : text background color
7735 * RETURN:
7736 * SUCCESS : TRUE
7737 * FAILURE : FALSE
7739 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7741 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7743 infoPtr->clrTextBk = clrTextBk;
7744 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7746 return TRUE;
7749 /***
7750 * DESCRIPTION:
7751 * Sets the text foreground color.
7753 * PARAMETER(S):
7754 * [I] infoPtr : valid pointer to the listview structure
7755 * [I] COLORREF : text color
7757 * RETURN:
7758 * SUCCESS : TRUE
7759 * FAILURE : FALSE
7761 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7763 TRACE("(clrText=%lx)\n", clrText);
7765 infoPtr->clrText = clrText;
7766 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7768 return TRUE;
7771 /* LISTVIEW_SetToolTips */
7772 /* LISTVIEW_SetUnicodeFormat */
7773 /* LISTVIEW_SetWorkAreas */
7775 /***
7776 * DESCRIPTION:
7777 * Callback internally used by LISTVIEW_SortItems()
7779 * PARAMETER(S):
7780 * [I] LPVOID : first LISTVIEW_ITEM to compare
7781 * [I] LPVOID : second LISTVIEW_ITEM to compare
7782 * [I] LPARAM : HWND of control
7784 * RETURN:
7785 * if first comes before second : negative
7786 * if first comes after second : positive
7787 * if first and second are equivalent : zero
7789 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7791 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7792 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7793 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7795 /* Forward the call to the client defined callback */
7796 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7799 /***
7800 * DESCRIPTION:
7801 * Sorts the listview items.
7803 * PARAMETER(S):
7804 * [I] infoPtr : valid pointer to the listview structure
7805 * [I] WPARAM : application-defined value
7806 * [I] LPARAM : pointer to comparision callback
7808 * RETURN:
7809 * SUCCESS : TRUE
7810 * FAILURE : FALSE
7812 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7814 UINT lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7815 HDPA hdpaSubItems=NULL;
7816 LISTVIEW_ITEM *pLVItem=NULL;
7817 LPVOID selectionMarkItem;
7818 int nCount, i;
7820 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7822 if (lStyle & LVS_OWNERDATA) return FALSE;
7824 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7826 nCount = GETITEMCOUNT(infoPtr);
7827 /* if there are 0 or 1 items, there is no need to sort */
7828 if (nCount < 2)
7829 return TRUE;
7831 infoPtr->pfnCompare = pfnCompare;
7832 infoPtr->lParamSort = lParamSort;
7833 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
7835 /* Adjust selections and indices so that they are the way they should
7836 * be after the sort (otherwise, the list items move around, but
7837 * whatever is at the item's previous original position will be
7838 * selected instead)
7840 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7841 for (i=0; i < nCount; i++)
7843 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7844 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7846 if (pLVItem->state & LVIS_SELECTED)
7847 LISTVIEW_AddSelectionRange(infoPtr, i, i);
7848 else
7849 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
7850 if (pLVItem->state & LVIS_FOCUSED)
7851 infoPtr->nFocusedItem=i;
7853 if (selectionMarkItem != NULL)
7854 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7855 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7857 /* align the items */
7858 LISTVIEW_AlignTop(infoPtr);
7860 /* refresh the display */
7861 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7863 return TRUE;
7866 /* LISTVIEW_SubItemHitTest */
7868 /***
7869 * DESCRIPTION:
7870 * Updates an items or rearranges the listview control.
7872 * PARAMETER(S):
7873 * [I] infoPtr : valid pointer to the listview structure
7874 * [I] INT : item index
7876 * RETURN:
7877 * SUCCESS : TRUE
7878 * FAILURE : FALSE
7880 static LRESULT LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7882 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
7883 BOOL bResult = FALSE;
7884 RECT rc;
7886 TRACE("(nItem=%d)\n", nItem);
7888 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7890 bResult = TRUE;
7892 /* rearrange with default alignment style */
7893 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7894 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7896 ListView_Arrange(infoPtr->hwndSelf, 0);
7898 else
7900 /* get item bounding rectangle */
7901 ListView_GetItemRect(infoPtr->hwndSelf, nItem, &rc, LVIR_BOUNDS);
7902 InvalidateRect(infoPtr->hwndSelf, &rc, TRUE);
7906 return bResult;
7909 /***
7910 * DESCRIPTION:
7911 * Creates the listview control.
7913 * PARAMETER(S):
7914 * [I] hwnd : window handle
7915 * [I] lpcs : the create parameters
7917 * RETURN:
7918 * Success: 0
7919 * Failure: -1
7921 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7923 LISTVIEW_INFO *infoPtr;
7924 UINT uView = lpcs->style & LVS_TYPEMASK;
7925 LOGFONTW logFont;
7927 TRACE("(lpcs=%p)\n", lpcs);
7929 /* initialize info pointer */
7930 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7931 if (!infoPtr) return -1;
7933 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7934 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7936 infoPtr->hwndSelf = hwnd;
7937 /* determine the type of structures to use */
7938 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7939 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7941 /* initialize color information */
7942 infoPtr->clrBk = comctl32_color.clrWindow;
7943 infoPtr->clrText = comctl32_color.clrWindowText;
7944 infoPtr->clrTextBk = CLR_DEFAULT;
7946 /* set default values */
7947 infoPtr->uCallbackMask = 0;
7948 infoPtr->nFocusedItem = -1;
7949 infoPtr->nSelectionMark = -1;
7950 infoPtr->nHotItem = -1;
7951 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7952 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7953 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7954 infoPtr->hwndEdit = 0;
7955 infoPtr->bEditing = FALSE;
7956 infoPtr->nEditLabelItem = -1;
7957 infoPtr->bIsDrawing = FALSE;
7959 /* get default font (icon title) */
7960 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7961 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7962 infoPtr->hFont = infoPtr->hDefaultFont;
7963 LISTVIEW_SaveTextMetrics(infoPtr);
7965 /* create header */
7966 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7967 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7968 0, 0, 0, 0, hwnd, (HMENU)0,
7969 lpcs->hInstance, NULL);
7971 /* set header unicode format */
7972 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
7974 /* set header font */
7975 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7976 (LPARAM)TRUE);
7978 if (uView == LVS_ICON)
7980 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7981 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7983 else if (uView == LVS_REPORT)
7985 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7987 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7989 else
7991 /* set HDS_HIDDEN flag to hide the header bar */
7992 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7993 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7997 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7998 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8000 else
8002 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8003 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8006 /* display unsupported listview window styles */
8007 LISTVIEW_UnsupportedStyles(lpcs->style);
8009 /* allocate memory for the data structure */
8010 infoPtr->hdpaItems = DPA_Create(10);
8012 /* allocate memory for the selection ranges */
8013 infoPtr->hdpaSelectionRanges = DPA_Create(10);
8015 /* initialize size of items */
8016 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8017 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8019 /* initialize the hover time to -1(indicating the default system hover time) */
8020 infoPtr->dwHoverTime = -1;
8022 return 0;
8025 /***
8026 * DESCRIPTION:
8027 * Erases the background of the listview control.
8029 * PARAMETER(S):
8030 * [I] infoPtr : valid pointer to the listview structure
8031 * [I] WPARAM : device context handle
8032 * [I] LPARAM : not used
8034 * RETURN:
8035 * SUCCESS : TRUE
8036 * FAILURE : FALSE
8038 static LRESULT LISTVIEW_EraseBackground(LISTVIEW_INFO *infoPtr, WPARAM wParam,
8039 LPARAM lParam)
8041 BOOL bResult;
8043 TRACE("(wParam=%x, lParam=%lx)\n", wParam, lParam);
8045 if (infoPtr->clrBk == CLR_NONE)
8047 bResult = SendMessageW(GetParent(infoPtr->hwndSelf), WM_ERASEBKGND, wParam, lParam);
8049 else
8051 RECT rc;
8052 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8053 GetClientRect(infoPtr->hwndSelf, &rc);
8054 FillRect((HDC)wParam, &rc, hBrush);
8055 DeleteObject(hBrush);
8056 bResult = TRUE;
8059 return bResult;
8063 static void LISTVIEW_FillBackground(LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rc)
8065 TRACE("(hdc=%x, rc=%p)\n", hdc, rc);
8067 if (infoPtr->clrBk != CLR_NONE)
8069 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8070 FillRect(hdc, rc, hBrush);
8071 DeleteObject(hBrush);
8075 /***
8076 * DESCRIPTION:
8077 * Performs vertical scrolling.
8079 * PARAMETER(S):
8080 * [I] infoPtr : valid pointer to the listview structure
8081 * [I] INT : scroll code
8082 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8083 * or SB_THUMBTRACK.
8084 * [I] HWND : scrollbar control window handle
8086 * RETURN:
8087 * Zero
8089 * NOTES:
8090 * SB_LINEUP/SB_LINEDOWN:
8091 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8092 * for LVS_REPORT is 1 line --> infoPtr->nItemHeight
8093 * for LVS_LIST cannot occur ??? (implemented as LVS_REPORT)
8096 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, SHORT nCurrentPos,
8097 HWND hScrollWnd)
8099 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
8100 SCROLLINFO scrollInfo;
8101 BOOL is_an_icon;
8103 TRACE("(nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8104 nScrollCode, nCurrentPos, hScrollWnd);
8106 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8108 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8109 scrollInfo.cbSize = sizeof(SCROLLINFO);
8110 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8112 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8114 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
8116 INT nOldScrollPos = scrollInfo.nPos;
8117 switch (nScrollCode)
8119 case SB_INTERNAL_UP:
8120 if (scrollInfo.nPos > scrollInfo.nMin)
8121 scrollInfo.nPos--;
8122 break;
8124 case SB_INTERNAL_DOWN:
8125 if (scrollInfo.nPos < scrollInfo.nMax)
8126 scrollInfo.nPos++;
8127 break;
8129 case SB_LINEUP:
8130 if (scrollInfo.nPos > scrollInfo.nMin)
8131 scrollInfo.nPos -= (is_an_icon) ?
8132 LISTVIEW_SCROLL_ICON_LINE_SIZE : infoPtr->nItemHeight;
8133 break;
8135 case SB_LINEDOWN:
8136 if (scrollInfo.nPos < scrollInfo.nMax)
8137 scrollInfo.nPos += (is_an_icon) ?
8138 LISTVIEW_SCROLL_ICON_LINE_SIZE : infoPtr->nItemHeight;
8139 break;
8141 case SB_PAGEUP:
8142 if (scrollInfo.nPos > scrollInfo.nMin)
8144 if (scrollInfo.nPos >= scrollInfo.nPage)
8145 scrollInfo.nPos -= scrollInfo.nPage;
8146 else
8147 scrollInfo.nPos = scrollInfo.nMin;
8149 break;
8151 case SB_PAGEDOWN:
8152 if (scrollInfo.nPos < scrollInfo.nMax)
8154 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8155 scrollInfo.nPos += scrollInfo.nPage;
8156 else
8157 scrollInfo.nPos = scrollInfo.nMax;
8159 break;
8161 case SB_THUMBPOSITION:
8162 case SB_THUMBTRACK:
8163 scrollInfo.nPos = nCurrentPos;
8164 if (scrollInfo.nPos > scrollInfo.nMax)
8165 scrollInfo.nPos=scrollInfo.nMax;
8167 if (scrollInfo.nPos < scrollInfo.nMin)
8168 scrollInfo.nPos=scrollInfo.nMin;
8170 break;
8173 if (nOldScrollPos != scrollInfo.nPos)
8175 scrollInfo.fMask = SIF_POS;
8176 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8178 /* Get real position value, the value we set might have been changed
8179 * by SetScrollInfo (especially if we went too far.
8181 scrollInfo.fMask = SIF_POS;
8182 GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo);
8184 /* only if the scroll position really changed, do we update screen */
8185 if (nOldScrollPos != scrollInfo.nPos)
8187 if (IsWindowVisible(infoPtr->hwndHeader))
8189 RECT rListview, rcHeader, rDest;
8190 GetClientRect(infoPtr->hwndSelf, &rListview);
8191 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8192 MapWindowPoints((HWND) NULL, infoPtr->hwndSelf, (LPPOINT) &rcHeader, 2);
8193 SubtractRect(&rDest, &rListview, &rcHeader);
8194 InvalidateRect(infoPtr->hwndSelf, &rDest, TRUE);
8196 else
8197 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8202 return 0;
8205 /***
8206 * DESCRIPTION:
8207 * Performs horizontal scrolling.
8209 * PARAMETER(S):
8210 * [I] infoPtr : valid pointer to the listview structure
8211 * [I] INT : scroll code
8212 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8213 * or SB_THUMBTRACK.
8214 * [I] HWND : scrollbar control window handle
8216 * RETURN:
8217 * Zero
8219 * NOTES:
8220 * SB_LINELEFT/SB_LINERIGHT:
8221 * for LVS_ICON, LVS_SMALLICON ??? (implemented as 1 pixel)
8222 * for LVS_REPORT is 1 pixel
8223 * for LVS_LIST is 1 column --> which is a 1 because the
8224 * scroll is based on columns not pixels
8227 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, SHORT nCurrentPos,
8228 HWND hScrollWnd)
8230 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
8231 SCROLLINFO scrollInfo;
8232 BOOL is_a_list;
8234 TRACE("(nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8235 nScrollCode, nCurrentPos, hScrollWnd);
8237 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8239 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8240 scrollInfo.cbSize = sizeof(SCROLLINFO);
8241 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8243 is_a_list = (uView == LVS_LIST);
8245 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
8247 INT nOldScrollPos = scrollInfo.nPos;
8249 switch (nScrollCode)
8251 case SB_INTERNAL_LEFT:
8252 if (scrollInfo.nPos > scrollInfo.nMin)
8253 scrollInfo.nPos--;
8254 break;
8256 case SB_INTERNAL_RIGHT:
8257 if (scrollInfo.nPos < scrollInfo.nMax)
8258 scrollInfo.nPos++;
8259 break;
8261 case SB_LINELEFT:
8262 if (scrollInfo.nPos > scrollInfo.nMin)
8263 scrollInfo.nPos--;
8264 break;
8266 case SB_LINERIGHT:
8267 if (scrollInfo.nPos < scrollInfo.nMax)
8268 scrollInfo.nPos++;
8269 break;
8271 case SB_PAGELEFT:
8272 if (scrollInfo.nPos > scrollInfo.nMin)
8274 if (scrollInfo.nPos >= scrollInfo.nPage)
8275 scrollInfo.nPos -= scrollInfo.nPage;
8276 else
8277 scrollInfo.nPos = scrollInfo.nMin;
8279 break;
8281 case SB_PAGERIGHT:
8282 if (scrollInfo.nPos < scrollInfo.nMax)
8284 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8285 scrollInfo.nPos += scrollInfo.nPage;
8286 else
8287 scrollInfo.nPos = scrollInfo.nMax;
8289 break;
8291 case SB_THUMBPOSITION:
8292 case SB_THUMBTRACK:
8293 scrollInfo.nPos = nCurrentPos;
8295 if (scrollInfo.nPos > scrollInfo.nMax)
8296 scrollInfo.nPos=scrollInfo.nMax;
8298 if (scrollInfo.nPos < scrollInfo.nMin)
8299 scrollInfo.nPos=scrollInfo.nMin;
8300 break;
8303 if (nOldScrollPos != scrollInfo.nPos)
8305 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
8306 scrollInfo.fMask = SIF_POS;
8307 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8309 /* Get real position value, the value we set might have been changed
8310 * by SetScrollInfo (especially if we went too far.
8312 scrollInfo.fMask = SIF_POS;
8313 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
8314 if(uView == LVS_REPORT)
8316 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
8319 /* only if the scroll position really changed, do we update screen */
8320 if (nOldScrollPos != scrollInfo.nPos)
8321 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8325 return 0;
8328 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8330 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
8331 INT gcWheelDelta = 0;
8332 UINT pulScrollLines = 3;
8333 SCROLLINFO scrollInfo;
8335 TRACE("(wheelDelta=%d)\n", wheelDelta);
8337 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8338 gcWheelDelta -= wheelDelta;
8340 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8341 scrollInfo.cbSize = sizeof(SCROLLINFO);
8342 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8344 switch(uView)
8346 case LVS_ICON:
8347 case LVS_SMALLICON:
8349 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8350 * should be fixed in the future.
8352 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
8353 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
8354 scrollInfo.nPos + (gcWheelDelta < 0) ?
8355 LISTVIEW_SCROLL_ICON_LINE_SIZE :
8356 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8357 break;
8359 case LVS_REPORT:
8360 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8362 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
8364 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8365 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8366 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8369 break;
8371 case LVS_LIST:
8372 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8373 break;
8375 return 0;
8378 /***
8379 * DESCRIPTION:
8380 * ???
8382 * PARAMETER(S):
8383 * [I] infoPtr : valid pointer to the listview structure
8384 * [I] INT : virtual key
8385 * [I] LONG : key data
8387 * RETURN:
8388 * Zero
8390 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8392 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
8393 INT nItem = -1;
8394 NMLVKEYDOWN nmKeyDown;
8396 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
8398 /* send LVN_KEYDOWN notification */
8399 nmKeyDown.wVKey = nVirtualKey;
8400 nmKeyDown.flags = 0;
8401 notify(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8403 switch (nVirtualKey)
8405 case VK_RETURN:
8406 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8408 notify_return(infoPtr);
8409 notify_itemactivate(infoPtr);
8411 break;
8413 case VK_HOME:
8414 if (GETITEMCOUNT(infoPtr) > 0)
8415 nItem = 0;
8416 break;
8418 case VK_END:
8419 if (GETITEMCOUNT(infoPtr) > 0)
8420 nItem = GETITEMCOUNT(infoPtr) - 1;
8421 break;
8423 case VK_LEFT:
8424 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8425 break;
8427 case VK_UP:
8428 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8429 break;
8431 case VK_RIGHT:
8432 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8433 break;
8435 case VK_DOWN:
8436 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8437 break;
8439 case VK_PRIOR:
8440 if (uView == LVS_REPORT)
8441 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
8442 else
8443 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8444 * LISTVIEW_GetCountPerRow(infoPtr);
8445 if(nItem < 0) nItem = 0;
8446 break;
8448 case VK_NEXT:
8449 if (uView == LVS_REPORT)
8450 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
8451 else
8452 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8453 * LISTVIEW_GetCountPerRow(infoPtr);
8454 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8455 break;
8458 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8460 if (LISTVIEW_KeySelection(infoPtr, nItem))
8461 UpdateWindow(infoPtr->hwndSelf); /* update client area */
8464 return 0;
8467 /***
8468 * DESCRIPTION:
8469 * Kills the focus.
8471 * PARAMETER(S):
8472 * [I] infoPtr : valid pointer to the listview structure
8474 * RETURN:
8475 * Zero
8477 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8479 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
8480 INT i,nTop,nBottom;
8482 TRACE("()\n");
8484 /* send NM_KILLFOCUS notification */
8485 notify_killfocus(infoPtr);
8487 /* set window focus flag */
8488 infoPtr->bFocus = FALSE;
8490 /* NEED drawing optimization ; redraw the selected items */
8491 if (uView & LVS_REPORT)
8493 nTop = LISTVIEW_GetTopIndex(infoPtr);
8494 nBottom = nTop +
8495 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8497 else
8499 nTop = 0;
8500 nBottom = GETITEMCOUNT(infoPtr);
8502 for (i = nTop; i<nBottom; i++)
8504 if (LISTVIEW_IsSelected(infoPtr,i))
8506 RECT rcItem;
8507 rcItem.left = LVIR_BOUNDS;
8508 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
8509 InvalidateRect(infoPtr->hwndSelf, &rcItem, FALSE);
8513 return 0;
8516 /***
8517 * DESCRIPTION:
8518 * Processes double click messages (left mouse button).
8520 * PARAMETER(S):
8521 * [I] infoPtr : valid pointer to the listview structure
8522 * [I] wKey : key flag
8523 * [I] pts : mouse coordinate
8525 * RETURN:
8526 * Zero
8528 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8530 LVHITTESTINFO htInfo;
8531 NMLISTVIEW nmlv;
8533 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8535 htInfo.pt.x = pts.x;
8536 htInfo.pt.y = pts.y;
8538 /* send NM_DBLCLK notification */
8539 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8540 if (LISTVIEW_HitTestItem(infoPtr, &htInfo, TRUE) != -1)
8542 nmlv.iItem = htInfo.iItem;
8543 nmlv.iSubItem = htInfo.iSubItem;
8545 else
8547 nmlv.iItem = -1;
8548 nmlv.iSubItem = 0;
8550 nmlv.ptAction.x = pts.x;
8551 nmlv.ptAction.y = pts.y;
8552 listview_notify(infoPtr, NM_DBLCLK, &nmlv);
8555 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8556 if(nmlv.iItem != -1)
8557 notify_itemactivate(infoPtr);
8559 return 0;
8562 /***
8563 * DESCRIPTION:
8564 * Processes mouse down messages (left mouse button).
8566 * PARAMETER(S):
8567 * [I] infoPtr : valid pointer to the listview structure
8568 * [I] wKey : key flag
8569 * [I] pts : mouse coordinate
8571 * RETURN:
8572 * Zero
8574 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8576 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
8577 static BOOL bGroupSelect = TRUE;
8578 POINT pt = { pts.x, pts.y };
8579 INT nItem;
8581 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8583 /* send NM_RELEASEDCAPTURE notification */
8584 notify_releasedcapture(infoPtr);
8586 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8588 /* set left button down flag */
8589 infoPtr->bLButtonDown = TRUE;
8591 nItem = LISTVIEW_MouseSelection(infoPtr, pt);
8592 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8594 if (lStyle & LVS_SINGLESEL)
8596 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8597 && infoPtr->nEditLabelItem == -1)
8598 infoPtr->nEditLabelItem = nItem;
8599 else
8600 LISTVIEW_SetSelection(infoPtr, nItem);
8602 else
8604 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8606 if (bGroupSelect)
8607 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8608 else
8609 LISTVIEW_AddSelection(infoPtr, nItem);
8611 else if (wKey & MK_CONTROL)
8613 bGroupSelect = LISTVIEW_ToggleSelection(infoPtr, nItem);
8615 else if (wKey & MK_SHIFT)
8617 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8619 else
8621 BOOL was_selected =
8622 (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8624 /* set selection (clears other pre-existing selections) */
8625 LISTVIEW_SetSelection(infoPtr, nItem);
8627 if (was_selected && infoPtr->nEditLabelItem == -1)
8628 infoPtr->nEditLabelItem = nItem;
8632 else
8634 /* remove all selections */
8635 LISTVIEW_RemoveAllSelections(infoPtr);
8638 /* redraw if we could have possibly selected something */
8639 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8641 return 0;
8644 /***
8645 * DESCRIPTION:
8646 * Processes mouse up messages (left mouse button).
8648 * PARAMETER(S):
8649 * [I] infoPtr : valid pointer to the listview structure
8650 * [I] wKey : key flag
8651 * [I] pts : mouse coordinate
8653 * RETURN:
8654 * Zero
8656 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8658 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8660 if (infoPtr->bLButtonDown)
8662 LVHITTESTINFO lvHitTestInfo;
8663 NMLISTVIEW nmlv;
8665 lvHitTestInfo.pt.x = pts.x;
8666 lvHitTestInfo.pt.y = pts.y;
8668 /* send NM_CLICK notification */
8669 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8670 if (LISTVIEW_HitTestItem(infoPtr, &lvHitTestInfo, TRUE) != -1)
8672 nmlv.iItem = lvHitTestInfo.iItem;
8673 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8675 else
8677 nmlv.iItem = -1;
8678 nmlv.iSubItem = 0;
8680 nmlv.ptAction.x = pts.x;
8681 nmlv.ptAction.y = pts.y;
8682 listview_notify(infoPtr, NM_CLICK, &nmlv);
8684 /* set left button flag */
8685 infoPtr->bLButtonDown = FALSE;
8687 if(infoPtr->nEditLabelItem != -1)
8689 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL) {
8690 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8692 infoPtr->nEditLabelItem = -1;
8696 return 0;
8699 /***
8700 * DESCRIPTION:
8701 * Destroys the listview control (called after WM_DESTROY).
8703 * PARAMETER(S):
8704 * [I] infoPtr : valid pointer to the listview structure
8706 * RETURN:
8707 * Zero
8709 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8711 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
8713 TRACE("()\n");
8715 /* delete all items */
8716 LISTVIEW_DeleteAllItems(infoPtr);
8718 /* destroy data structure */
8719 DPA_Destroy(infoPtr->hdpaItems);
8720 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8722 /* destroy image lists */
8723 if (!(lStyle & LVS_SHAREIMAGELISTS))
8725 #if 0
8726 /* FIXME: If the caller does a ImageList_Destroy and then we
8727 * do this code the area will be freed twice. Currently
8728 * this generates an "err:heap:HEAP_ValidateInUseArena
8729 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
8730 * has PREV_FREE flag" sometimes.
8732 * We will leak the memory till we figure out how to fix
8734 if (infoPtr->himlNormal)
8735 ImageList_Destroy(infoPtr->himlNormal);
8736 if (infoPtr->himlSmall)
8737 ImageList_Destroy(infoPtr->himlSmall);
8738 if (infoPtr->himlState)
8739 ImageList_Destroy(infoPtr->himlState);
8740 #endif
8743 /* destroy font */
8744 infoPtr->hFont = (HFONT)0;
8745 if (infoPtr->hDefaultFont)
8747 DeleteObject(infoPtr->hDefaultFont);
8750 /* free listview info pointer*/
8751 COMCTL32_Free(infoPtr);
8753 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
8754 return 0;
8757 /***
8758 * DESCRIPTION:
8759 * Handles notifications from children.
8761 * PARAMETER(S):
8762 * [I] infoPtr : valid pointer to the listview structure
8763 * [I] INT : control identifier
8764 * [I] LPNMHDR : notification information
8766 * RETURN:
8767 * Zero
8769 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
8771 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
8773 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8775 /* handle notification from header control */
8776 if (lpnmh->code == HDN_ENDTRACKW)
8778 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8779 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8781 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
8783 /* Handle sorting by Header Column */
8784 NMLISTVIEW nmlv;
8786 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8787 nmlv.iItem = -1;
8788 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8789 listview_notify(infoPtr, LVN_COLUMNCLICK, &nmlv);
8791 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8793 /* Idealy this should be done in HDN_ENDTRACKA
8794 * but since SetItemBounds in Header.c is called after
8795 * the notification is sent, it is neccessary to handle the
8796 * update of the scroll bar here (Header.c works fine as it is,
8797 * no need to disturb it)
8799 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
8800 LISTVIEW_UpdateScroll(infoPtr);
8801 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8806 return 0;
8809 /***
8810 * DESCRIPTION:
8811 * Determines the type of structure to use.
8813 * PARAMETER(S):
8814 * [I] infoPtr : valid pointer to the listview structureof the sender
8815 * [I] HWND : listview window handle
8816 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8818 * RETURN:
8819 * Zero
8821 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8823 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
8825 if (nCommand == NF_REQUERY)
8826 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8827 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8828 return 0;
8831 /***
8832 * DESCRIPTION:
8833 * Paints/Repaints the listview control.
8835 * PARAMETER(S):
8836 * [I] infoPtr : valid pointer to the listview structure
8837 * [I] HDC : device context handle
8839 * RETURN:
8840 * Zero
8842 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8844 PAINTSTRUCT ps;
8846 TRACE("(hdc=%x)\n", hdc);
8848 if (hdc == 0)
8850 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8851 LISTVIEW_Refresh(infoPtr, hdc);
8852 EndPaint(infoPtr->hwndSelf, &ps);
8854 else
8856 LISTVIEW_Refresh(infoPtr, hdc);
8859 return 0;
8862 /***
8863 * DESCRIPTION:
8864 * Processes double click messages (right mouse button).
8866 * PARAMETER(S):
8867 * [I] infoPtr : valid pointer to the listview structure
8868 * [I] wKey : key flag
8869 * [I] pts : mouse coordinate
8871 * RETURN:
8872 * Zero
8874 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8876 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8878 /* send NM_RELEASEDCAPTURE notification */
8879 notify_releasedcapture(infoPtr);
8881 /* send NM_RDBLCLK notification */
8882 notify_rdblclk(infoPtr);
8884 return 0;
8887 /***
8888 * DESCRIPTION:
8889 * Processes mouse down messages (right mouse button).
8891 * PARAMETER(S):
8892 * [I] infoPtr : valid pointer to the listview structure
8893 * [I] wKey : key flag
8894 * [I] pts : mouse coordinate
8896 * RETURN:
8897 * Zero
8899 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8901 INT nItem;
8902 NMLISTVIEW nmlv;
8903 LVHITTESTINFO lvHitTestInfo;
8904 POINT pt = { pts.x, pts.y };
8906 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8908 /* send NM_RELEASEDCAPTURE notification */
8909 notify_releasedcapture(infoPtr);
8911 /* make sure the listview control window has the focus */
8912 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8914 /* set right button down flag */
8915 infoPtr->bRButtonDown = TRUE;
8917 /* determine the index of the selected item */
8918 nItem = LISTVIEW_MouseSelection(infoPtr, pt);
8919 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8921 LISTVIEW_SetItemFocus(infoPtr,nItem);
8922 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8923 !LISTVIEW_IsSelected(infoPtr,nItem))
8924 LISTVIEW_SetSelection(infoPtr, nItem);
8926 else
8928 LISTVIEW_RemoveAllSelections(infoPtr);
8931 lvHitTestInfo.pt.x = pts.x;
8932 lvHitTestInfo.pt.y = pts.y;
8934 /* Send NM_RClICK notification */
8935 ZeroMemory(&nmlv, sizeof(nmlv));
8936 if (LISTVIEW_HitTestItem(infoPtr, &lvHitTestInfo, TRUE) != -1)
8938 nmlv.iItem = lvHitTestInfo.iItem;
8939 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8941 else
8943 nmlv.iItem = -1;
8944 nmlv.iSubItem = 0;
8946 nmlv.ptAction.x = pts.x;
8947 nmlv.ptAction.y = pts.y;
8948 listview_notify(infoPtr, NM_RCLICK, &nmlv);
8950 return 0;
8953 /***
8954 * DESCRIPTION:
8955 * Processes mouse up messages (right mouse button).
8957 * PARAMETER(S):
8958 * [I] infoPtr : valid pointer to the listview structure
8959 * [I] wKey : key flag
8960 * [I] pts : mouse coordinate
8962 * RETURN:
8963 * Zero
8965 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8967 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8969 if (infoPtr->bRButtonDown)
8971 POINT pt = { pts.x, pts.y };
8973 /* set button flag */
8974 infoPtr->bRButtonDown = FALSE;
8976 /* Change to screen coordinate for WM_CONTEXTMENU */
8977 ClientToScreen(infoPtr->hwndSelf, &pt);
8979 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8980 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8981 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8984 return 0;
8988 /***
8989 * DESCRIPTION:
8990 * Sets the cursor.
8992 * PARAMETER(S):
8993 * [I] infoPtr : valid pointer to the listview structure
8994 * [I] hwnd : window handle of window containing the cursor
8995 * [I] nHittest : hit-test code
8996 * [I] wMouseMsg : ideintifier of the mouse message
8998 * RETURN:
8999 * TRUE if cursor is set
9000 * FALSE otherwise
9002 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9004 POINT pt;
9006 if(!(infoPtr->dwExStyle & LVS_EX_TRACKSELECT)) return FALSE;
9008 GetCursorPos(&pt);
9009 if (LISTVIEW_MouseSelection(infoPtr, pt) < 0) return FALSE;
9011 SetCursor(infoPtr->hHotCursor);
9013 return TRUE;
9016 /***
9017 * DESCRIPTION:
9018 * Sets the focus.
9020 * PARAMETER(S):
9021 * [I] infoPtr : valid pointer to the listview structure
9022 * [I] infoPtr : valid pointer to the listview structureof previously focused window
9024 * RETURN:
9025 * Zero
9027 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9029 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
9031 /* send NM_SETFOCUS notification */
9032 notify_setfocus(infoPtr);
9034 /* set window focus flag */
9035 infoPtr->bFocus = TRUE;
9037 UpdateWindow(infoPtr->hwndSelf);
9039 return 0;
9042 /***
9043 * DESCRIPTION:
9044 * Sets the font.
9046 * PARAMETER(S):
9047 * [I] infoPtr : valid pointer to the listview structure
9048 * [I] HFONT : font handle
9049 * [I] WORD : redraw flag
9051 * RETURN:
9052 * Zero
9054 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9056 UINT uView = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & LVS_TYPEMASK;
9058 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
9060 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9061 LISTVIEW_SaveTextMetrics(infoPtr);
9063 if (uView == LVS_REPORT)
9065 /* set header font */
9066 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9067 MAKELPARAM(fRedraw, 0));
9070 /* invalidate listview control client area */
9071 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9073 if (fRedraw) UpdateWindow(infoPtr->hwndSelf);
9075 return 0;
9078 /***
9079 * DESCRIPTION:
9080 * Message handling for WM_SETREDRAW.
9081 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9083 * PARAMETER(S):
9084 * [I] infoPtr : valid pointer to the listview structure
9085 * [I] bRedraw: state of redraw flag
9087 * RETURN:
9088 * DefWinProc return value
9090 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9092 LRESULT lResult = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, bRedraw, 0);
9093 if(bRedraw)
9094 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
9095 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9096 return lResult;
9099 /***
9100 * DESCRIPTION:
9101 * Resizes the listview control. This function processes WM_SIZE
9102 * messages. At this time, the width and height are not used.
9104 * PARAMETER(S):
9105 * [I] infoPtr : valid pointer to the listview structure
9106 * [I] WORD : new width
9107 * [I] WORD : new height
9109 * RETURN:
9110 * Zero
9112 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9114 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
9115 UINT uView = lStyle & LVS_TYPEMASK;
9117 TRACE("(width=%d, height=%d)\n", Width, Height);
9119 if (LISTVIEW_UpdateSize(infoPtr))
9121 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9123 if (lStyle & LVS_ALIGNLEFT)
9124 LISTVIEW_AlignLeft(infoPtr);
9125 else
9126 LISTVIEW_AlignTop(infoPtr);
9129 LISTVIEW_UpdateScroll(infoPtr);
9131 /* invalidate client area + erase background */
9132 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9135 return 0;
9138 /***
9139 * DESCRIPTION:
9140 * Sets the size information.
9142 * PARAMETER(S):
9143 * [I] infoPtr : valid pointer to the listview structure
9145 * RETURN:
9146 * Zero if no size change
9147 * 1 of size changed
9149 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9151 LONG lStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
9152 UINT uView = lStyle & LVS_TYPEMASK;
9153 RECT rcList;
9154 RECT rcOld;
9156 GetClientRect(infoPtr->hwndSelf, &rcList);
9157 CopyRect(&rcOld,&(infoPtr->rcList));
9158 infoPtr->rcList.left = 0;
9159 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9160 infoPtr->rcList.top = 0;
9161 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9163 if (uView == LVS_LIST)
9165 /* Apparently the "LIST" style is supposed to have the same
9166 * number of items in a column even if there is no scroll bar.
9167 * Since if a scroll bar already exists then the bottom is already
9168 * reduced, only reduce if the scroll bar does not currently exist.
9169 * The "2" is there to mimic the native control. I think it may be
9170 * related to either padding or edges. (GLA 7/2002)
9172 if (!(lStyle & WS_HSCROLL))
9174 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9175 if (infoPtr->rcList.bottom > nHScrollHeight)
9176 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
9178 else
9180 if (infoPtr->rcList.bottom > 2)
9181 infoPtr->rcList.bottom -= 2;
9184 else if (uView == LVS_REPORT)
9186 HDLAYOUT hl;
9187 WINDOWPOS wp;
9189 hl.prc = &rcList;
9190 hl.pwpos = &wp;
9191 Header_Layout(infoPtr->hwndHeader, &hl);
9193 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9195 if (!(LVS_NOCOLUMNHEADER & lStyle))
9196 infoPtr->rcList.top = max(wp.cy, 0);
9198 return (EqualRect(&rcOld,&(infoPtr->rcList)));
9201 /***
9202 * DESCRIPTION:
9203 * Processes WM_STYLECHANGED messages.
9205 * PARAMETER(S):
9206 * [I] infoPtr : valid pointer to the listview structure
9207 * [I] WPARAM : window style type (normal or extended)
9208 * [I] LPSTYLESTRUCT : window style information
9210 * RETURN:
9211 * Zero
9213 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9214 LPSTYLESTRUCT lpss)
9216 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9217 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9218 RECT rcList = infoPtr->rcList;
9220 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
9221 wStyleType, lpss->styleOld, lpss->styleNew);
9223 if (wStyleType == GWL_STYLE)
9225 if (uOldView == LVS_REPORT)
9226 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9228 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9229 ((lpss->styleNew & WS_HSCROLL) == 0))
9230 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9232 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9233 ((lpss->styleNew & WS_VSCROLL) == 0))
9234 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9236 /* If switching modes, then start with no scroll bars and then
9237 * decide.
9239 if (uNewView != uOldView)
9240 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9242 if (uNewView == LVS_ICON)
9244 INT oldcx, oldcy;
9246 /* First readjust the iconSize and if necessary the iconSpacing */
9247 oldcx = infoPtr->iconSize.cx;
9248 oldcy = infoPtr->iconSize.cy;
9249 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9250 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9251 if (infoPtr->himlNormal != NULL)
9253 INT cx, cy;
9254 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
9255 infoPtr->iconSize.cx = cx;
9256 infoPtr->iconSize.cy = cy;
9258 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
9260 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
9261 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9262 LISTVIEW_SetIconSpacing(infoPtr,0);
9265 /* Now update the full item width and height */
9266 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
9267 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
9268 if (lpss->styleNew & LVS_ALIGNLEFT)
9269 LISTVIEW_AlignLeft(infoPtr);
9270 else
9271 LISTVIEW_AlignTop(infoPtr);
9273 else if (uNewView == LVS_REPORT)
9275 HDLAYOUT hl;
9276 WINDOWPOS wp;
9278 hl.prc = &rcList;
9279 hl.pwpos = &wp;
9280 Header_Layout(infoPtr->hwndHeader, &hl);
9281 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9282 wp.flags);
9283 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9284 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9286 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9287 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9288 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
9289 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
9291 else if (uNewView == LVS_LIST)
9293 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9294 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9295 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
9296 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
9298 else
9300 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9301 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9302 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(infoPtr);
9303 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
9304 if (lpss->styleNew & LVS_ALIGNLEFT)
9305 LISTVIEW_AlignLeft(infoPtr);
9306 else
9307 LISTVIEW_AlignTop(infoPtr);
9310 /* update the size of the client area */
9311 LISTVIEW_UpdateSize(infoPtr);
9313 /* add scrollbars if needed */
9314 LISTVIEW_UpdateScroll(infoPtr);
9316 /* invalidate client area + erase background */
9317 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9319 /* print the list of unsupported window styles */
9320 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9323 /* If they change the view and we have an active edit control
9324 we will need to kill the control since the redraw will
9325 misplace the edit control.
9327 if (infoPtr->bEditing &&
9328 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9329 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9331 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9334 return 0;
9337 /***
9338 * DESCRIPTION:
9339 * Window procedure of the listview control.
9342 static LRESULT WINAPI
9343 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9345 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9347 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9349 if (!infoPtr && (uMsg != WM_CREATE))
9350 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9352 switch (uMsg)
9354 case LVM_APPROXIMATEVIEWRECT:
9355 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9356 LOWORD(lParam), HIWORD(lParam));
9357 case LVM_ARRANGE:
9358 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9360 /* case LVN_CANCELEDITLABEL */
9362 /* case LVM_CREATEDRAGIMAGE: */
9364 case LVM_DELETEALLITEMS:
9365 return LISTVIEW_DeleteAllItems(infoPtr);
9367 case LVM_DELETECOLUMN:
9368 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9370 case LVM_DELETEITEM:
9371 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9373 case LVM_EDITLABELW:
9374 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9376 case LVM_EDITLABELA:
9377 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9379 /* case LVN_ENABLEGROUPVIEW: */
9381 case LVM_ENSUREVISIBLE:
9382 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9384 case LVM_FINDITEMW:
9385 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9387 case LVM_FINDITEMA:
9388 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9390 case LVM_GETBKCOLOR:
9391 return infoPtr->clrBk;
9393 /* case LVM_GETBKIMAGE: */
9395 case LVM_GETCALLBACKMASK:
9396 return LISTVIEW_GetCallbackMask(infoPtr);
9398 case LVM_GETCOLUMNA:
9399 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9401 case LVM_GETCOLUMNW:
9402 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9404 case LVM_GETCOLUMNORDERARRAY:
9405 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9407 case LVM_GETCOLUMNWIDTH:
9408 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9410 case LVM_GETCOUNTPERPAGE:
9411 return LISTVIEW_GetCountPerPage(infoPtr);
9413 case LVM_GETEDITCONTROL:
9414 return (LRESULT)infoPtr->hwndEdit;
9416 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9417 return infoPtr->dwExStyle;
9419 case LVM_GETHEADER:
9420 return (LRESULT)infoPtr->hwndHeader;
9422 case LVM_GETHOTCURSOR:
9423 return infoPtr->hHotCursor;
9425 case LVM_GETHOTITEM:
9426 return infoPtr->nHotItem;
9428 case LVM_GETHOVERTIME:
9429 return infoPtr->dwHoverTime;
9431 case LVM_GETIMAGELIST:
9432 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9434 /* case LVN_GETINSERTMARK: */
9436 /* case LVN_GETINSERTMARKCOLOR: */
9438 /* case LVN_GETINSERTMARKRECT: */
9440 case LVM_GETISEARCHSTRINGA:
9441 case LVM_GETISEARCHSTRINGW:
9442 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9443 return FALSE;
9445 case LVM_GETITEMA:
9446 return LISTVIEW_GetItemT(infoPtr, (LPLVITEMW)lParam, FALSE, FALSE);
9448 case LVM_GETITEMW:
9449 return LISTVIEW_GetItemT(infoPtr, (LPLVITEMW)lParam, FALSE, TRUE);
9451 case LVM_GETITEMCOUNT:
9452 return GETITEMCOUNT(infoPtr);
9454 case LVM_GETITEMPOSITION:
9455 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9457 case LVM_GETITEMRECT:
9458 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9460 case LVM_GETITEMSPACING:
9461 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9463 case LVM_GETITEMSTATE:
9464 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9466 case LVM_GETITEMTEXTA:
9467 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9469 case LVM_GETITEMTEXTW:
9470 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9472 case LVM_GETNEXTITEM:
9473 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9475 case LVM_GETNUMBEROFWORKAREAS:
9476 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9477 return 1;
9479 case LVM_GETORIGIN:
9480 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9482 /* case LVN_GETOUTLINECOLOR: */
9484 /* case LVM_GETSELECTEDCOLUMN: */
9486 case LVM_GETSELECTEDCOUNT:
9487 return LISTVIEW_GetSelectedCount(infoPtr);
9489 case LVM_GETSELECTIONMARK:
9490 return infoPtr->nSelectionMark;
9492 case LVM_GETSTRINGWIDTHA:
9493 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9495 case LVM_GETSTRINGWIDTHW:
9496 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9498 case LVM_GETSUBITEMRECT:
9499 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, ((LPRECT)lParam)->top,
9500 ((LPRECT)lParam)->left, (LPRECT)lParam);
9502 case LVM_GETTEXTBKCOLOR:
9503 return LISTVIEW_GetTextBkColor(infoPtr);
9505 case LVM_GETTEXTCOLOR:
9506 return LISTVIEW_GetTextColor(infoPtr);
9508 /* case LVN_GETTILEINFO: */
9510 /* case LVN_GETTILEVIEWINFO: */
9512 case LVM_GETTOOLTIPS:
9513 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9514 return FALSE;
9516 case LVM_GETTOPINDEX:
9517 return LISTVIEW_GetTopIndex(infoPtr);
9519 /*case LVM_GETUNICODEFORMAT:
9520 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9521 return FALSE;*/
9523 case LVM_GETVIEWRECT:
9524 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9526 case LVM_GETWORKAREAS:
9527 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9528 return FALSE;
9530 /* case LVN_HASGROUP: */
9532 case LVM_HITTEST:
9533 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam);
9535 case LVM_INSERTCOLUMNA:
9536 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9538 case LVM_INSERTCOLUMNW:
9539 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9541 /* case LVN_INSERTGROUP: */
9543 /* case LVN_INSERTGROUPSORTED: */
9545 case LVM_INSERTITEMA:
9546 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9548 case LVM_INSERTITEMW:
9549 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9551 /* case LVN_INSERTMARKHITTEST: */
9553 /* case LVN_ISGROUPVIEWENABLED: */
9555 /* case LVN_MAPIDTOINDEX: */
9557 /* case LVN_INEDXTOID: */
9559 /* case LVN_MOVEGROUP: */
9561 /* case LVN_MOVEITEMTOGROUP: */
9563 case LVM_REDRAWITEMS:
9564 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9566 /* case LVN_REMOVEALLGROUPS: */
9568 /* case LVN_REMOVEGROUP: */
9570 case LVM_SCROLL:
9571 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9573 case LVM_SETBKCOLOR:
9574 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9576 /* case LVM_SETBKIMAGE: */
9578 case LVM_SETCALLBACKMASK:
9579 return LISTVIEW_SetCallbackMask(infoPtr, (UINT)wParam);
9581 case LVM_SETCOLUMNA:
9582 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9584 case LVM_SETCOLUMNW:
9585 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9587 case LVM_SETCOLUMNORDERARRAY:
9588 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9590 case LVM_SETCOLUMNWIDTH:
9591 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
9593 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9594 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9596 /* case LVN_SETGROUPINFO: */
9598 /* case LVN_SETGROUPMETRICS: */
9600 case LVM_SETHOTCURSOR:
9601 return LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9603 case LVM_SETHOTITEM:
9604 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9606 case LVM_SETHOVERTIME:
9607 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9609 case LVM_SETICONSPACING:
9610 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
9612 case LVM_SETIMAGELIST:
9613 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9615 /* case LVN_SETINFOTIP: */
9617 /* case LVN_SETINSERTMARK: */
9619 /* case LVN_SETINSERTMARKCOLOR: */
9621 case LVM_SETITEMA:
9622 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9624 case LVM_SETITEMW:
9625 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9627 case LVM_SETITEMCOUNT:
9628 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9630 case LVM_SETITEMPOSITION:
9631 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (INT)LOWORD(lParam),
9632 (INT)HIWORD(lParam));
9634 case LVM_SETITEMPOSITION32:
9635 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, ((POINT*)lParam)->x,
9636 ((POINT*)lParam)->y);
9638 case LVM_SETITEMSTATE:
9639 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9641 case LVM_SETITEMTEXTA:
9642 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9644 case LVM_SETITEMTEXTW:
9645 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9647 /* case LVN_SETOUTLINECOLOR: */
9649 /* case LVN_SETSELECTEDCOLUMN: */
9651 case LVM_SETSELECTIONMARK:
9652 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9654 case LVM_SETTEXTBKCOLOR:
9655 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9657 case LVM_SETTEXTCOLOR:
9658 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9660 /* case LVN_SETTILEINFO: */
9662 /* case LVN_SETTILEVIEWINFO: */
9664 /* case LVN_SETTILEWIDTH: */
9666 /* case LVM_SETTOOLTIPS: */
9668 /* case LVM_SETUNICODEFORMAT: */
9670 /* case LVN_SETVIEW: */
9672 /* case LVM_SETWORKAREAS: */
9674 /* case LVN_SORTGROUPS: */
9676 case LVM_SORTITEMS:
9677 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9679 case LVM_SUBITEMHITTEST:
9680 return LISTVIEW_SubItemHitTest(infoPtr, (LPLVHITTESTINFO)lParam);
9682 case LVM_UPDATE:
9683 return LISTVIEW_Update(infoPtr, (INT)wParam);
9685 case WM_CHAR:
9686 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9688 case WM_COMMAND:
9689 return LISTVIEW_Command(infoPtr, wParam, lParam);
9691 case WM_CREATE:
9692 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9694 case WM_ERASEBKGND:
9695 return LISTVIEW_EraseBackground(infoPtr, wParam, lParam);
9697 case WM_GETDLGCODE:
9698 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9700 case WM_GETFONT:
9701 return infoPtr->hFont;
9703 case WM_HSCROLL:
9704 if (SLOWORD(wParam) < 0) return 0; /* validate not internal codes */
9705 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam),
9706 (INT)HIWORD(wParam), (HWND)lParam);
9708 case WM_KEYDOWN:
9709 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9711 case WM_KILLFOCUS:
9712 return LISTVIEW_KillFocus(infoPtr);
9714 case WM_LBUTTONDBLCLK:
9715 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9717 case WM_LBUTTONDOWN:
9718 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9720 case WM_LBUTTONUP:
9721 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9723 case WM_MOUSEMOVE:
9724 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9726 case WM_MOUSEHOVER:
9727 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9729 case WM_NCDESTROY:
9730 return LISTVIEW_NCDestroy(infoPtr);
9732 case WM_NOTIFY:
9733 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
9735 case WM_NOTIFYFORMAT:
9736 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9738 case WM_PAINT:
9739 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9741 case WM_RBUTTONDBLCLK:
9742 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9744 case WM_RBUTTONDOWN:
9745 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9747 case WM_RBUTTONUP:
9748 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9750 case WM_SETCURSOR:
9751 return LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam));
9753 case WM_SETFOCUS:
9754 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9756 case WM_SETFONT:
9757 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9759 case WM_SETREDRAW:
9760 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9762 case WM_SIZE:
9763 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9765 case WM_STYLECHANGED:
9766 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9768 case WM_SYSCOLORCHANGE:
9769 COMCTL32_RefreshSysColors();
9770 return 0;
9772 /* case WM_TIMER: */
9774 case WM_VSCROLL:
9775 if (SLOWORD(wParam) < 0) return 0; /* validate not internal codes */
9776 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam),
9777 (INT)HIWORD(wParam), (HWND)lParam);
9779 case WM_MOUSEWHEEL:
9780 if (wParam & (MK_SHIFT | MK_CONTROL))
9781 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9782 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9784 case WM_WINDOWPOSCHANGED:
9785 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
9786 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9787 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9788 LISTVIEW_UpdateSize(infoPtr);
9789 LISTVIEW_UpdateScroll(infoPtr);
9791 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9793 /* case WM_WININICHANGE: */
9795 default:
9796 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9797 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9799 /* call default window procedure */
9800 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9803 return 0;
9806 /***
9807 * DESCRIPTION:
9808 * Registers the window class.
9810 * PARAMETER(S):
9811 * None
9813 * RETURN:
9814 * None
9816 VOID LISTVIEW_Register(void)
9818 WNDCLASSW wndClass;
9820 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9821 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9822 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9823 wndClass.cbClsExtra = 0;
9824 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9825 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9826 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9827 wndClass.lpszClassName = WC_LISTVIEWW;
9828 RegisterClassW(&wndClass);
9831 /***
9832 * DESCRIPTION:
9833 * Unregisters the window class.
9835 * PARAMETER(S):
9836 * None
9838 * RETURN:
9839 * None
9841 VOID LISTVIEW_Unregister(void)
9843 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9846 /***
9847 * DESCRIPTION:
9848 * Handle any WM_COMMAND messages
9850 * PARAMETER(S):
9852 * RETURN:
9854 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9856 switch (HIWORD(wParam))
9858 case EN_UPDATE:
9861 * Adjust the edit window size
9863 WCHAR buffer[1024];
9864 HDC hdc = GetDC(infoPtr->hwndEdit);
9865 HFONT hFont, hOldFont = 0;
9866 RECT rect;
9867 SIZE sz;
9868 int len;
9870 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9871 GetWindowRect(infoPtr->hwndEdit, &rect);
9873 /* Select font to get the right dimension of the string */
9874 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9875 if(hFont != 0)
9877 hOldFont = SelectObject(hdc, hFont);
9880 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9882 TEXTMETRICW textMetric;
9884 /* Add Extra spacing for the next character */
9885 GetTextMetricsW(hdc, &textMetric);
9886 sz.cx += (textMetric.tmMaxCharWidth * 2);
9888 SetWindowPos (
9889 infoPtr->hwndEdit,
9890 HWND_TOP,
9893 sz.cx,
9894 rect.bottom - rect.top,
9895 SWP_DRAWFRAME|SWP_NOMOVE);
9897 if(hFont != 0)
9898 SelectObject(hdc, hOldFont);
9900 ReleaseDC(infoPtr->hwndSelf, hdc);
9902 break;
9905 default:
9906 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
9909 return 0;
9913 /***
9914 * DESCRIPTION:
9915 * Subclassed edit control windproc function
9917 * PARAMETER(S):
9919 * RETURN:
9921 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9922 WPARAM wParam, LPARAM lParam, BOOL isW)
9924 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9925 static BOOL bIgnoreKillFocus = FALSE;
9926 BOOL cancel = FALSE;
9928 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9929 hwnd, uMsg, wParam, lParam, isW);
9931 switch (uMsg)
9933 case WM_GETDLGCODE:
9934 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9936 case WM_KILLFOCUS:
9937 if(bIgnoreKillFocus) return TRUE;
9938 break;
9940 case WM_DESTROY:
9942 WNDPROC editProc = infoPtr->EditWndProc;
9943 infoPtr->EditWndProc = 0;
9944 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9945 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9948 case WM_KEYDOWN:
9949 if (VK_ESCAPE == (INT)wParam)
9951 cancel = TRUE;
9952 break;
9954 else if (VK_RETURN == (INT)wParam)
9955 break;
9957 default:
9958 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9961 if (infoPtr->bEditing)
9963 LPWSTR buffer = NULL;
9965 if (!cancel)
9967 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9969 if (len)
9971 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9973 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9974 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9978 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9979 /* eg. Using a messagebox */
9980 bIgnoreKillFocus = TRUE;
9981 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9983 if (buffer) COMCTL32_Free(buffer);
9985 bIgnoreKillFocus = FALSE;
9988 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9989 return TRUE;
9992 /***
9993 * DESCRIPTION:
9994 * Subclassed edit control windproc function
9996 * PARAMETER(S):
9998 * RETURN:
10000 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10002 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10005 /***
10006 * DESCRIPTION:
10007 * Subclassed edit control windproc function
10009 * PARAMETER(S):
10011 * RETURN:
10013 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10015 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10018 /***
10019 * DESCRIPTION:
10020 * Creates a subclassed edit cotrol
10022 * PARAMETER(S):
10024 * RETURN:
10026 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10027 INT x, INT y, INT width, INT height, BOOL isW)
10029 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10030 HWND hedit;
10031 SIZE sz;
10032 HDC hdc;
10033 HDC hOldFont=0;
10034 TEXTMETRICW textMetric;
10035 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
10037 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
10039 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
10040 hdc = GetDC(infoPtr->hwndSelf);
10042 /* Select the font to get appropriate metric dimensions */
10043 if(infoPtr->hFont != 0)
10044 hOldFont = SelectObject(hdc, infoPtr->hFont);
10046 /*Get String Lenght in pixels */
10047 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10049 /*Add Extra spacing for the next character */
10050 GetTextMetricsW(hdc, &textMetric);
10051 sz.cx += (textMetric.tmMaxCharWidth * 2);
10053 if(infoPtr->hFont != 0)
10054 SelectObject(hdc, hOldFont);
10056 ReleaseDC(infoPtr->hwndSelf, hdc);
10057 if (isW)
10058 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10059 else
10060 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10062 if (!hedit) return 0;
10064 infoPtr->EditWndProc = (WNDPROC)
10065 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
10066 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
10068 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
10070 return hedit;