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
25 * Listview control implementation.
28 * 1. No horizontal scrolling when header is larger than the client area.
29 * 2. Drawing optimizations.
30 * 3. Hot item handling.
33 * LISTVIEW_Notify : most notifications from children (editbox and header)
36 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
38 * Advanced functionality:
39 * LISTVIEW_GetNumberOfWorkAreas : not implemented
40 * LISTVIEW_GetHotCursor : not implemented
41 * LISTVIEW_GetISearchString : not implemented
42 * LISTVIEW_GetBkImage : not implemented
43 * LISTVIEW_SetBkImage : not implemented
44 * LISTVIEW_GetColumnOrderArray : simple hack only
45 * LISTVIEW_SetColumnOrderArray : simple hack only
46 * LISTVIEW_Arrange : empty stub
47 * LISTVIEW_ApproximateViewRect : incomplete
48 * LISTVIEW_Scroll : not implemented
49 * LISTVIEW_Update : not completed
51 * Known differences in message stream from native control (not known if
52 * these differences cause problems):
53 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
54 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
55 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
56 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
57 * does *not* invoke DefWindowProc
58 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
59 * processing for "USEDOUBLECLICKTIME".
71 #include "wine/debug.h"
73 WINE_DEFAULT_DEBUG_CHANNEL(listview);
75 /* Some definitions for inline edit control */
76 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
77 typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
79 typedef struct tagLV_INTHIT
82 DWORD distance; /* distance to closest item */
83 INT iDistItem; /* item number that is closest */
84 } LV_INTHIT, *LPLV_INTHIT;
87 typedef struct tagEDITLABEL_ITEM
91 EditlblCallbackW EditLblCb;
94 typedef struct tagLISTVIEW_SUBITEM
101 typedef struct tagLISTVIEW_ITEM
112 typedef struct tagLISTVIEW_SELECTION
116 } LISTVIEW_SELECTION;
118 typedef struct tagLISTVIEW_INFO
124 HIMAGELIST himlNormal;
125 HIMAGELIST himlSmall;
126 HIMAGELIST himlState;
130 HDPA hdpaSelectionRanges;
144 INT ntmHeight; /* from GetTextMetrics from above font */
145 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
147 DWORD dwExStyle; /* extended listview style */
149 PFNLVCOMPARE pfnCompare;
153 EDITLABEL_ITEM *pedititem;
155 INT nColumnCount; /* the number of columns in this control */
157 DWORD lastKeyPressTimestamp; /* Added */
158 WPARAM charCode; /* Added */
159 INT nSearchParamLength; /* Added */
160 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
168 /* maximum size of a label */
169 #define DISP_TEXT_SIZE 512
171 /* padding for items in list and small icon display modes */
172 #define WIDTH_PADDING 12
174 /* padding for items in list, report and small icon display modes */
175 #define HEIGHT_PADDING 1
177 /* offset of items in report display mode */
178 #define REPORT_MARGINX 2
180 /* padding for icon in large icon display mode
181 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
182 * that HITTEST will see.
183 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
184 * ICON_TOP_PADDING - sum of the two above.
185 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
186 * LABEL_VERT_OFFSET - between bottom of text and end of box
188 #define ICON_TOP_PADDING_NOTHITABLE 2
189 #define ICON_TOP_PADDING_HITABLE 2
190 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
191 #define ICON_BOTTOM_PADDING 4
192 #define LABEL_VERT_OFFSET 10
194 /* default label width for items in list and small icon display modes */
195 #define DEFAULT_LABEL_WIDTH 40
197 /* default column width for items in list display mode */
198 #define DEFAULT_COLUMN_WIDTH 96
200 /* Increment size of the horizontal scroll bar */
201 #define LISTVIEW_SCROLL_DIV_SIZE 10
203 /* Padding betwen image and label */
204 #define IMAGE_PADDING 2
206 /* Padding behind the label */
207 #define TRAILING_PADDING 5
209 /* Border for the icon caption */
210 #define CAPTION_BORDER 2
214 /* retrieve the number of items in the listview */
215 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
216 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
218 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
219 INT width, INT height, HWND parent, HINSTANCE hinst,
220 EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
223 * forward declarations
225 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
226 static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
227 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
228 static INT LISTVIEW_GetCountPerRow(HWND);
229 static INT LISTVIEW_GetCountPerColumn(HWND);
230 static VOID LISTVIEW_AlignLeft(HWND);
231 static VOID LISTVIEW_AlignTop(HWND);
232 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
233 static VOID LISTVIEW_AddSelection(HWND, INT);
234 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
235 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
236 static INT LISTVIEW_GetItemHeight(HWND);
237 static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
238 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
239 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
240 static LRESULT LISTVIEW_GetSubItemRect(HWND, INT, INT, INT, LPRECT);
241 static INT LISTVIEW_GetItemWidth(HWND);
242 static INT LISTVIEW_GetLabelWidth(HWND, INT);
243 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
244 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
245 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
246 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
247 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
248 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
249 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
250 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
251 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
252 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
253 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
254 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
255 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
256 static VOID LISTVIEW_UpdateScroll(HWND);
257 static VOID LISTVIEW_SetSelection(HWND, INT);
258 static BOOL LISTVIEW_UpdateSize(HWND);
259 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
260 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
261 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
262 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
263 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
264 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
265 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
266 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
267 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
268 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
269 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
270 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
271 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
272 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
273 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
274 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
275 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
276 static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
277 static LRESULT LISTVIEW_GetColumnT(HWND, INT, LPLVCOLUMNW, BOOL);
279 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
280 #define KEY_DELAY 450
282 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
284 static inline BOOL is_textW(LPCWSTR text)
286 return text != NULL && text != LPSTR_TEXTCALLBACKW;
289 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
291 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
292 return is_textW(text);
295 static inline int textlenT(LPCWSTR text, BOOL isW)
297 return !is_textT(text, isW) ? 0 :
298 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
301 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
304 if (isSrcW) lstrcpynW(dest, src, max);
305 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
307 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
308 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
311 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
313 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
316 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
318 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
321 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
323 LPWSTR wstr = (LPWSTR)text;
325 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
328 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
329 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
330 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
332 TRACE(" wstr=%s\n", debugstr_w(wstr));
336 static inline void textfreeT(LPWSTR wstr, BOOL isW)
338 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
342 * dest is a pointer to a Unicode string
343 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
345 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
347 LPWSTR pszText = textdupTtoW(src, isW);
349 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
350 bResult = Str_SetPtrW(dest, pszText);
351 textfreeT(pszText, isW);
355 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
356 WPARAM wParam, LPARAM lParam, BOOL isW)
359 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
361 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
364 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
366 pnmh->hwndFrom = self;
367 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
369 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
370 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
373 static inline BOOL hdr_notify(HWND self, INT code)
376 return notify(self, code, &nmh);
379 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
381 return notify(self, code, (LPNMHDR)plvnm);
384 static int tabNotification[] = {
385 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
386 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
387 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
388 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
389 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
390 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
394 static int get_ansi_notification(INT unicodeNotificationCode)
396 int *pTabNotif = tabNotification;
397 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
398 if (*pTabNotif) return *pTabNotif;
399 ERR("unknown notification %x\n", unicodeNotificationCode);
400 return unicodeNotificationCode;
404 Send notification. depends on dispinfoW having same
405 structure as dispinfoA.
406 self : listview handle
407 notificationCode : *Unicode* notification code
408 pdi : dispinfo structure (can be unicode or ansi)
409 isW : TRUE if dispinfo is Unicode
411 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
413 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
414 BOOL bResult = FALSE;
415 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
417 INT cchTempBufMax = 0, savCchTextMax = 0;
418 LPWSTR pszTempBuf = NULL, savPszText = NULL;
420 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
421 TRACE(" notifyFormat=%s\n",
422 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
423 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
424 if (infoPtr->notifyFormat == NFR_ANSI)
425 realNotifCode = get_ansi_notification(notificationCode);
427 realNotifCode = notificationCode;
429 if (is_textT(pdi->item.pszText, isW))
431 if (isW && infoPtr->notifyFormat == NFR_ANSI)
432 convertToAnsi = TRUE;
433 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
434 convertToUnicode = TRUE;
437 if (convertToAnsi || convertToUnicode)
439 TRACE(" we have to convert the text to the correct format\n");
440 if (notificationCode != LVN_GETDISPINFOW)
441 { /* length of existing text */
442 cchTempBufMax = convertToUnicode ?
443 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
444 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
447 cchTempBufMax = pdi->item.cchTextMax;
449 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
450 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
451 if (!pszTempBuf) return FALSE;
452 if (convertToUnicode)
453 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
454 pszTempBuf, cchTempBufMax);
456 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
457 cchTempBufMax, NULL, NULL);
458 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
459 savCchTextMax = pdi->item.cchTextMax;
460 savPszText = pdi->item.pszText;
461 pdi->item.pszText = pszTempBuf;
462 pdi->item.cchTextMax = cchTempBufMax;
465 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
467 if (convertToUnicode || convertToAnsi)
468 { /* convert back result */
469 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
470 if (convertToUnicode) /* note : pointer can be changed by app ! */
471 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
472 savCchTextMax, NULL, NULL);
474 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
475 savPszText, savCchTextMax);
476 pdi->item.pszText = savPszText; /* restores our buffer */
477 pdi->item.cchTextMax = savCchTextMax;
478 HeapFree(GetProcessHeap(), 0, pszTempBuf);
483 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
485 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
488 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
492 n = min(min(n, strlenW(s1)), strlenW(s2));
493 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
494 return res ? res - 2 : res;
497 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
499 static int index = 0;
500 static char buffers[20][256];
501 char* buf = buffers[index++ % 20];
502 if (lpLVItem == NULL) return "(null)";
503 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
504 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
505 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
506 lpLVItem->state, lpLVItem->stateMask,
507 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
508 debugstr_tn(lpLVItem->pszText, isW, 80),
509 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
514 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
516 static int index = 0;
517 static char buffers[20][256];
518 char* buf = buffers[index++ % 20];
519 if (lpColumn == NULL) return "(null)";
520 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
521 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
522 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
523 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
524 debugstr_tn(lpColumn->pszText, isW, 80): "",
525 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
529 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
531 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
532 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
533 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
534 iP->nItemHeight, iP->nItemWidth, dwStyle);
535 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
536 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
537 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
541 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
544 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
545 NMLVCUSTOMDRAW nmcdhdr;
548 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
550 nmcd= & nmcdhdr.nmcd;
551 nmcd->hdr.hwndFrom = hwnd;
552 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
553 nmcd->hdr.code = NM_CUSTOMDRAW;
554 nmcd->dwDrawStage= dwDrawStage;
556 nmcd->rc.left = rc.left;
557 nmcd->rc.right = rc.right;
558 nmcd->rc.bottom = rc.bottom;
559 nmcd->rc.top = rc.top;
560 nmcd->dwItemSpec = 0;
561 nmcd->uItemState = 0;
562 nmcd->lItemlParam= 0;
563 nmcdhdr.clrText = infoPtr->clrText;
564 nmcdhdr.clrTextBk= infoPtr->clrBk;
566 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
567 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
571 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
572 UINT iItem, UINT iSubItem,
575 LISTVIEW_INFO *infoPtr;
576 NMLVCUSTOMDRAW nmcdhdr;
578 DWORD dwDrawStage,dwItemSpec;
584 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
586 ZeroMemory(&item,sizeof(item));
588 item.mask = LVIF_PARAM;
589 ListView_GetItemW(hwnd,&item);
591 dwDrawStage=CDDS_ITEM | uItemDrawState;
595 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
596 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
597 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
599 itemRect.left = LVIR_BOUNDS;
600 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
602 nmcd= & nmcdhdr.nmcd;
603 nmcd->hdr.hwndFrom = hwnd;
604 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
605 nmcd->hdr.code = NM_CUSTOMDRAW;
606 nmcd->dwDrawStage= dwDrawStage;
608 nmcd->rc.left = itemRect.left;
609 nmcd->rc.right = itemRect.right;
610 nmcd->rc.bottom = itemRect.bottom;
611 nmcd->rc.top = itemRect.top;
612 nmcd->dwItemSpec = dwItemSpec;
613 nmcd->uItemState = uItemState;
614 nmcd->lItemlParam= item.lParam;
615 nmcdhdr.clrText = infoPtr->clrText;
616 nmcdhdr.clrTextBk= infoPtr->clrBk;
617 nmcdhdr.iSubItem =iSubItem;
619 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
620 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
621 nmcd->uItemState, nmcd->lItemlParam);
623 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
624 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
626 infoPtr->clrText=nmcdhdr.clrText;
627 infoPtr->clrBk =nmcdhdr.clrTextBk;
628 return (BOOL) retval;
632 /*************************************************************************
633 * LISTVIEW_ProcessLetterKeys
635 * Processes keyboard messages generated by pressing the letter keys
637 * What this does is perform a case insensitive search from the
638 * current position with the following quirks:
639 * - If two chars or more are pressed in quick succession we search
640 * for the corresponding string (e.g. 'abc').
641 * - If there is a delay we wipe away the current search string and
642 * restart with just that char.
643 * - If the user keeps pressing the same character, whether slowly or
644 * fast, so that the search string is entirely composed of this
645 * character ('aaaaa' for instance), then we search for first item
646 * that starting with that character.
647 * - If the user types the above character in quick succession, then
648 * we must also search for the corresponding string ('aaaaa'), and
649 * go to that string if there is a match.
657 * - The current implementation has a list of characters it will
658 * accept and it ignores averything else. In particular it will
659 * ignore accentuated characters which seems to match what
660 * Windows does. But I'm not sure it makes sense to follow
662 * - We don't sound a beep when the search fails.
666 * TREEVIEW_ProcessLetterKeys
668 static INT LISTVIEW_ProcessLetterKeys(
669 HWND hwnd, /* handle to the window */
670 WPARAM charCode, /* the character code, the actual character */
671 LPARAM keyData /* key data */
674 LISTVIEW_INFO *infoPtr;
679 WCHAR buffer[MAX_PATH];
680 DWORD timestamp,elapsed;
682 /* simple parameter checking */
683 if (!hwnd || !charCode || !keyData)
686 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
690 /* only allow the valid WM_CHARs through */
691 if (!isalnum(charCode) &&
692 charCode != '.' && charCode != '`' && charCode != '!' &&
693 charCode != '@' && charCode != '#' && charCode != '$' &&
694 charCode != '%' && charCode != '^' && charCode != '&' &&
695 charCode != '*' && charCode != '(' && charCode != ')' &&
696 charCode != '-' && charCode != '_' && charCode != '+' &&
697 charCode != '=' && charCode != '\\'&& charCode != ']' &&
698 charCode != '}' && charCode != '[' && charCode != '{' &&
699 charCode != '/' && charCode != '?' && charCode != '>' &&
700 charCode != '<' && charCode != ',' && charCode != '~')
703 nSize=GETITEMCOUNT(infoPtr);
704 /* if there's one item or less, there is no where to go */
708 /* compute how much time elapsed since last keypress */
709 timestamp=GetTickCount();
710 if (timestamp > infoPtr->lastKeyPressTimestamp) {
711 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
713 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
716 /* update the search parameters */
717 infoPtr->lastKeyPressTimestamp=timestamp;
718 if (elapsed < KEY_DELAY) {
719 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
720 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
722 if (infoPtr->charCode != charCode) {
723 infoPtr->charCode=charCode=0;
726 infoPtr->charCode=charCode;
727 infoPtr->szSearchParam[0]=charCode;
728 infoPtr->nSearchParamLength=1;
729 /* Redundant with the 1 char string */
733 /* and search from the current position */
735 if (infoPtr->nFocusedItem >= 0) {
736 endidx=infoPtr->nFocusedItem;
738 /* if looking for single character match,
739 * then we must always move forward
741 if (infoPtr->nSearchParamLength == 1)
755 ZeroMemory(&item, sizeof(item));
756 item.mask = LVIF_TEXT;
759 item.pszText = buffer;
760 item.cchTextMax = COUNTOF(buffer);
761 ListView_GetItemW( hwnd, &item );
763 /* check for a match */
764 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
767 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
768 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
769 /* This would work but we must keep looking for a longer match */
773 } while (idx != endidx);
776 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
777 /* refresh client area */
778 InvalidateRect(hwnd, NULL, TRUE);
786 /*************************************************************************
787 * LISTVIEW_UpdateHeaderSize [Internal]
789 * Function to resize the header control
792 * hwnd [I] handle to a window
793 * nNewScrollPos [I] Scroll Pos to Set
800 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
802 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
806 GetWindowRect(infoPtr->hwndHeader, &winRect);
807 point[0].x = winRect.left;
808 point[0].y = winRect.top;
809 point[1].x = winRect.right;
810 point[1].y = winRect.bottom;
812 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
813 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
814 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
816 SetWindowPos(infoPtr->hwndHeader,0,
817 point[0].x,point[0].y,point[1].x,point[1].y,
818 SWP_NOZORDER | SWP_NOACTIVATE);
823 * Update the scrollbars. This functions should be called whenever
824 * the content, size or view changes.
827 * [I] HWND : window handle
832 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
834 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
835 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
836 UINT uView = lStyle & LVS_TYPEMASK;
837 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
838 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
839 SCROLLINFO scrollInfo;
841 if (lStyle & LVS_NOSCROLL) return;
843 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
844 scrollInfo.cbSize = sizeof(SCROLLINFO);
846 if (uView == LVS_LIST)
848 /* update horizontal scrollbar */
850 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
851 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
852 INT nNumOfItems = GETITEMCOUNT(infoPtr);
854 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
855 if((nNumOfItems % nCountPerColumn) == 0)
863 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
864 scrollInfo.nPage = nCountPerRow;
865 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
866 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
867 ShowScrollBar(hwnd, SB_VERT, FALSE);
869 else if (uView == LVS_REPORT)
871 /* update vertical scrollbar */
873 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
874 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
875 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
876 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
877 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
879 /* update horizontal scrollbar */
880 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
881 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
882 || GETITEMCOUNT(infoPtr) == 0)
887 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
888 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
889 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
890 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
892 /* Update the Header Control */
893 scrollInfo.fMask = SIF_POS;
894 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
895 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
902 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
904 INT nViewWidth = rcView.right - rcView.left;
905 INT nViewHeight = rcView.bottom - rcView.top;
907 /* Update Horizontal Scrollbar */
908 scrollInfo.fMask = SIF_POS;
909 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
910 || GETITEMCOUNT(infoPtr) == 0)
914 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
916 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
917 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
918 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
920 /* Update Vertical Scrollbar */
921 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
922 scrollInfo.fMask = SIF_POS;
923 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
924 || GETITEMCOUNT(infoPtr) == 0)
928 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
930 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
931 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
932 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
939 * Prints a message for unsupported window styles.
940 * A kind of TODO list for window styles.
943 * [I] LONG : window style
948 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
950 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
951 FIXME(" LVS_NOSCROLL\n");
953 if (lStyle & LVS_EDITLABELS)
954 FIXME(" LVS_EDITLABELS\n");
956 if (lStyle & LVS_NOLABELWRAP)
957 FIXME(" LVS_NOLABELWRAP\n");
959 if (lStyle & LVS_SHAREIMAGELISTS)
960 FIXME(" LVS_SHAREIMAGELISTS\n");
962 if (lStyle & LVS_SORTASCENDING)
963 FIXME(" LVS_SORTASCENDING\n");
965 if (lStyle & LVS_SORTDESCENDING)
966 FIXME(" LVS_SORTDESCENDING\n");
971 * Aligns the items with the top edge of the window.
974 * [I] HWND : window handle
979 static VOID LISTVIEW_AlignTop(HWND hwnd)
981 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
982 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
983 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
986 INT i, off_x=0, off_y=0;
988 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
990 /* Since SetItemPosition uses upper-left of icon, and for
991 style=LVS_ICON the icon is not left adjusted, get the offset */
992 if (uView == LVS_ICON)
994 off_y = ICON_TOP_PADDING;
995 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
999 ZeroMemory(&rcView, sizeof(RECT));
1001 if (nListWidth > infoPtr->nItemWidth)
1003 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1005 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
1008 ptItem.y += infoPtr->nItemHeight;
1011 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1012 ptItem.x += infoPtr->nItemWidth;
1013 rcView.right = max(rcView.right, ptItem.x);
1016 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
1020 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1022 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1023 ptItem.y += infoPtr->nItemHeight;
1026 rcView.right = infoPtr->nItemWidth;
1027 rcView.bottom = ptItem.y;
1030 LISTVIEW_SetViewRect(hwnd, &rcView);
1036 * Aligns the items with the left edge of the window.
1039 * [I] HWND : window handle
1044 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1046 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1047 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1048 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1051 INT i, off_x=0, off_y=0;
1053 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1055 /* Since SetItemPosition uses upper-left of icon, and for
1056 style=LVS_ICON the icon is not left adjusted, get the offset */
1057 if (uView == LVS_ICON)
1059 off_y = ICON_TOP_PADDING;
1060 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1064 ZeroMemory(&rcView, sizeof(RECT));
1066 if (nListHeight > infoPtr->nItemHeight)
1068 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1070 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1073 ptItem.x += infoPtr->nItemWidth;
1076 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1077 ptItem.y += infoPtr->nItemHeight;
1078 rcView.bottom = max(rcView.bottom, ptItem.y);
1081 rcView.right = ptItem.x + infoPtr->nItemWidth;
1085 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1087 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1088 ptItem.x += infoPtr->nItemWidth;
1091 rcView.bottom = infoPtr->nItemHeight;
1092 rcView.right = ptItem.x;
1095 LISTVIEW_SetViewRect(hwnd, &rcView);
1101 * Set the bounding rectangle of all the items.
1104 * [I] HWND : window handle
1105 * [I] LPRECT : bounding rectangle
1111 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1113 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1114 BOOL bResult = FALSE;
1116 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1117 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1119 if (lprcView != NULL)
1122 infoPtr->rcView.left = lprcView->left;
1123 infoPtr->rcView.top = lprcView->top;
1124 infoPtr->rcView.right = lprcView->right;
1125 infoPtr->rcView.bottom = lprcView->bottom;
1133 * Retrieves the bounding rectangle of all the items.
1136 * [I] HWND : window handle
1137 * [O] LPRECT : bounding rectangle
1143 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1145 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1146 BOOL bResult = FALSE;
1149 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1151 if (lprcView != NULL)
1153 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1154 if (bResult != FALSE)
1156 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1157 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1158 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1159 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1162 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1163 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1171 * Retrieves the subitem pointer associated with the subitem index.
1174 * [I] HDPA : DPA handle for a specific item
1175 * [I] INT : index of subitem
1178 * SUCCESS : subitem pointer
1181 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1184 LISTVIEW_SUBITEM *lpSubItem;
1187 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1189 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1190 if (lpSubItem != NULL)
1192 if (lpSubItem->iSubItem == nSubItem)
1204 * Calculates the width of an item.
1207 * [I] HWND : window handle
1208 * [I] LONG : window style
1211 * Returns item width.
1213 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1215 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1216 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1217 UINT uView = style & LVS_TYPEMASK;
1218 INT nHeaderItemCount;
1224 TRACE("(hwnd=%x)\n", hwnd);
1226 if (uView == LVS_ICON)
1228 nItemWidth = infoPtr->iconSpacing.cx;
1230 else if (uView == LVS_REPORT)
1232 /* calculate width of header */
1233 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1234 for (i = 0; i < nHeaderItemCount; i++)
1236 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1238 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1244 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1246 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1247 nItemWidth = max(nItemWidth, nLabelWidth);
1250 /* default label size */
1251 if (GETITEMCOUNT(infoPtr) == 0)
1253 nItemWidth = DEFAULT_COLUMN_WIDTH;
1257 if (nItemWidth == 0)
1259 nItemWidth = DEFAULT_LABEL_WIDTH;
1264 nItemWidth += WIDTH_PADDING;
1266 if (infoPtr->himlSmall != NULL)
1268 nItemWidth += infoPtr->iconSize.cx;
1271 if (infoPtr->himlState != NULL)
1273 nItemWidth += infoPtr->iconSize.cx;
1280 /* nItemWidth Cannot be Zero */
1288 * Calculates the width of a specific item.
1291 * [I] HWND : window handle
1292 * [I] LPSTR : string
1295 * Returns the width of an item width a specified string.
1297 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1299 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1300 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1301 INT nHeaderItemCount;
1306 TRACE("(hwnd=%x)\n", hwnd);
1308 if (uView == LVS_ICON)
1310 nItemWidth = infoPtr->iconSpacing.cx;
1312 else if (uView == LVS_REPORT)
1314 /* calculate width of header */
1315 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1316 for (i = 0; i < nHeaderItemCount; i++)
1318 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1320 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1326 /* get width of string */
1327 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1329 /* default label size */
1330 if (GETITEMCOUNT(infoPtr) == 0)
1332 nItemWidth = DEFAULT_COLUMN_WIDTH;
1336 if (nItemWidth == 0)
1338 nItemWidth = DEFAULT_LABEL_WIDTH;
1343 nItemWidth += WIDTH_PADDING;
1345 if (infoPtr->himlSmall != NULL)
1347 nItemWidth += infoPtr->iconSize.cx;
1350 if (infoPtr->himlState != NULL)
1352 nItemWidth += infoPtr->iconSize.cx;
1363 * Retrieves and saves important text metrics info for the current
1367 * [I] HWND : window handle
1370 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1372 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1374 HDC hdc = GetDC(hwnd);
1375 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1376 INT oldHeight, oldACW;
1378 GetTextMetricsW(hdc, &tm);
1380 oldHeight = infoPtr->ntmHeight;
1381 oldACW = infoPtr->ntmAveCharWidth;
1382 infoPtr->ntmHeight = tm.tmHeight;
1383 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1385 SelectObject(hdc, hOldFont);
1386 ReleaseDC(hwnd, hdc);
1387 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1388 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1394 * Calculates the height of an item.
1397 * [I] HWND : window handle
1400 * Returns item height.
1402 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1404 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1405 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1406 INT nItemHeight = 0;
1408 if (uView == LVS_ICON)
1410 nItemHeight = infoPtr->iconSpacing.cy;
1414 if(infoPtr->himlState || infoPtr->himlSmall)
1415 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1417 nItemHeight = infoPtr->ntmHeight;
1424 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1426 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1427 LISTVIEW_SELECTION *selection;
1428 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1431 TRACE("Selections are:\n");
1432 for (i = 0; i < topSelection; i++)
1434 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1435 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1441 * A compare function for selection ranges
1444 * [I] LPVOID : Item 1;
1445 * [I] LPVOID : Item 2;
1446 * [I] LPARAM : flags
1449 * >0 : if Item 1 > Item 2
1450 * <0 : if Item 2 > Item 1
1451 * 0 : if Item 1 == Item 2
1453 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1456 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1457 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1458 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1459 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1473 * Adds a selection range.
1476 * [I] HWND : window handle
1477 * [I] INT : lower item index
1478 * [I] INT : upper item index
1483 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1485 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1486 LISTVIEW_SELECTION *selection;
1487 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1488 BOOL lowerzero=FALSE;
1490 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1491 selection->lower = lItem;
1492 selection->upper = uItem;
1494 TRACE("Add range %i - %i\n", lItem, uItem);
1497 LISTVIEW_SELECTION *checkselection,*checkselection2;
1498 INT index,mergeindex;
1500 /* find overlapping selections */
1501 /* we want to catch adjacent ranges so expand our range by 1 */
1504 if (selection->lower == 0)
1509 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1510 LISTVIEW_CompareSelectionRanges,
1512 selection->upper --;
1516 selection->lower ++;
1520 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1521 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1522 checkselection->upper);
1524 checkselection->lower = min(selection->lower,checkselection->lower);
1525 checkselection->upper = max(selection->upper,checkselection->upper);
1527 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1528 checkselection->upper);
1530 COMCTL32_Free(selection);
1532 /* merge now common selection ranges in the lower group*/
1535 checkselection->upper ++;
1536 if (checkselection->lower == 0)
1539 checkselection->lower --;
1541 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1542 checkselection->upper);
1544 /* not sorted yet */
1545 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1546 LISTVIEW_CompareSelectionRanges, 0,
1549 checkselection->upper --;
1553 checkselection->lower ++;
1555 if (mergeindex >=0 && mergeindex != index)
1557 TRACE("Merge with index %i\n",mergeindex);
1558 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1560 checkselection->lower = min(checkselection->lower,
1561 checkselection2->lower);
1562 checkselection->upper = max(checkselection->upper,
1563 checkselection2->upper);
1564 COMCTL32_Free(checkselection2);
1565 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1569 while (mergeindex > -1 && mergeindex <index);
1571 /* merge now common selection ranges in the upper group*/
1574 checkselection->upper ++;
1575 if (checkselection->lower == 0)
1578 checkselection->lower --;
1580 TRACE("search upper range %i (%lu - %lu)\n",index,
1581 checkselection->lower, checkselection->upper);
1583 /* not sorted yet */
1584 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1586 LISTVIEW_CompareSelectionRanges, 0,
1589 checkselection->upper --;
1593 checkselection->lower ++;
1595 if (mergeindex >=0 && mergeindex !=index)
1597 TRACE("Merge with index %i\n",mergeindex);
1598 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1600 checkselection->lower = min(checkselection->lower,
1601 checkselection2->lower);
1602 checkselection->upper = max(checkselection->upper,
1603 checkselection2->upper);
1604 COMCTL32_Free(checkselection2);
1605 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1608 while (mergeindex > -1);
1613 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1614 LISTVIEW_CompareSelectionRanges, 0,
1617 TRACE("Insert before index %i\n",index);
1620 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1625 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1630 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1631 LISTVIEW_PrintSelectionRanges(hwnd);
1636 * check if a specified index is selected.
1639 * [I] HWND : window handle
1640 * [I] INT : item index
1645 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1647 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1648 LISTVIEW_SELECTION selection;
1651 selection.upper = nItem;
1652 selection.lower = nItem;
1654 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1655 LISTVIEW_CompareSelectionRanges,
1665 * Removes all selection ranges
1668 * HWND: window handle
1674 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1676 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1677 LISTVIEW_SELECTION *selection;
1681 TRACE("(0x%x)\n",hwnd);
1683 ZeroMemory(&item,sizeof(item));
1684 item.stateMask = LVIS_SELECTED;
1688 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1691 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1692 for (i = selection->lower; i<=selection->upper; i++)
1693 LISTVIEW_SetItemState(hwnd,i,&item);
1694 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1697 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1705 * Removes a range selections.
1708 * [I] HWND : window handle
1709 * [I] INT : lower item index
1710 * [I] INT : upper item index
1715 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1717 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1718 LISTVIEW_SELECTION removeselection,*checkselection;
1721 removeselection.lower = lItem;
1722 removeselection.upper = uItem;
1724 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1725 LISTVIEW_PrintSelectionRanges(hwnd);
1727 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1728 LISTVIEW_CompareSelectionRanges,
1735 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1738 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1739 checkselection->upper);
1742 if ((checkselection->upper == removeselection.upper) &&
1743 (checkselection->lower == removeselection.lower))
1745 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1748 /* case 2: engulf */
1749 else if (((checkselection->upper < removeselection.upper) &&
1750 (checkselection->lower > removeselection.lower))||
1751 ((checkselection->upper <= removeselection.upper) &&
1752 (checkselection->lower > removeselection.lower)) ||
1753 ((checkselection->upper < removeselection.upper) &&
1754 (checkselection->lower >= removeselection.lower)))
1757 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1758 /* do it again because others may also get caught */
1760 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1762 /* case 3: overlap upper */
1763 else if ((checkselection->upper < removeselection.upper) &&
1764 (checkselection->lower < removeselection.lower))
1766 checkselection->upper = removeselection.lower - 1;
1768 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1770 /* case 4: overlap lower */
1771 else if ((checkselection->upper > removeselection.upper) &&
1772 (checkselection->lower > removeselection.lower))
1774 checkselection->lower = removeselection.upper + 1;
1776 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1778 /* case 5: fully internal */
1779 else if (checkselection->upper == removeselection.upper)
1780 checkselection->upper = removeselection.lower - 1;
1781 else if (checkselection->lower == removeselection.lower)
1782 checkselection->lower = removeselection.upper + 1;
1785 /* bisect the range */
1786 LISTVIEW_SELECTION *newselection;
1788 newselection = (LISTVIEW_SELECTION *)
1789 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1790 newselection -> lower = checkselection->lower;
1791 newselection -> upper = removeselection.lower - 1;
1792 checkselection -> lower = removeselection.upper + 1;
1793 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1795 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1797 LISTVIEW_PrintSelectionRanges(hwnd);
1802 * Updates the various indices after an item has been inserted or deleted.
1805 * [I] HWND : window handle
1806 * [I] INT : item index
1807 * [I] INT : Direction of shift, +1 or -1.
1812 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1814 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1815 LISTVIEW_SELECTION selection,*checkselection;
1818 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1820 selection.upper = nItem;
1821 selection.lower = nItem;
1823 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1824 LISTVIEW_CompareSelectionRanges,
1825 0,DPAS_SORTED|DPAS_INSERTAFTER);
1827 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1829 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1830 if ((checkselection->lower >= nItem)&&
1831 (checkselection->lower + direction >= 0))
1832 checkselection->lower += direction;
1833 if ((checkselection->upper >= nItem)&&
1834 (checkselection->upper + direction >=0))
1835 checkselection->upper += direction;
1839 /* Note that the following will fail if direction != +1 and -1 */
1840 if (infoPtr->nSelectionMark > nItem)
1841 infoPtr->nSelectionMark += direction;
1842 else if (infoPtr->nSelectionMark == nItem)
1845 infoPtr->nSelectionMark += direction;
1846 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1847 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1850 if (infoPtr->nFocusedItem > nItem)
1851 infoPtr->nFocusedItem += direction;
1852 else if (infoPtr->nFocusedItem == nItem)
1855 infoPtr->nFocusedItem += direction;
1858 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1859 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1860 if (infoPtr->nFocusedItem >= 0)
1861 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1864 /* But we are not supposed to modify nHotItem! */
1870 * Adds a block of selections.
1873 * [I] HWND : window handle
1874 * [I] INT : item index
1879 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1881 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1882 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1883 INT nLast = max(infoPtr->nSelectionMark, nItem);
1890 ZeroMemory(&item,sizeof(item));
1891 item.stateMask = LVIS_SELECTED;
1892 item.state = LVIS_SELECTED;
1894 for (i = nFirst; i <= nLast; i++)
1895 LISTVIEW_SetItemState(hwnd,i,&item);
1897 LISTVIEW_SetItemFocus(hwnd, nItem);
1898 infoPtr->nSelectionMark = nItem;
1904 * Adds a single selection.
1907 * [I] HWND : window handle
1908 * [I] INT : item index
1913 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1915 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1918 ZeroMemory(&item,sizeof(item));
1919 item.state = LVIS_SELECTED;
1920 item.stateMask = LVIS_SELECTED;
1922 LISTVIEW_SetItemState(hwnd,nItem,&item);
1924 LISTVIEW_SetItemFocus(hwnd, nItem);
1925 infoPtr->nSelectionMark = nItem;
1930 * Selects or unselects an item.
1933 * [I] HWND : window handle
1934 * [I] INT : item index
1940 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1942 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1946 ZeroMemory(&item,sizeof(item));
1947 item.stateMask = LVIS_SELECTED;
1949 if (LISTVIEW_IsSelected(hwnd,nItem))
1951 LISTVIEW_SetItemState(hwnd,nItem,&item);
1956 item.state = LVIS_SELECTED;
1957 LISTVIEW_SetItemState(hwnd,nItem,&item);
1961 LISTVIEW_SetItemFocus(hwnd, nItem);
1962 infoPtr->nSelectionMark = nItem;
1969 * Selects items based on view coordinates.
1972 * [I] HWND : window handle
1973 * [I] RECT : selection rectangle
1978 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1980 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1985 ZeroMemory(&item,sizeof(item));
1986 item.stateMask = LVIS_SELECTED;
1988 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1990 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1992 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1993 item.state = LVIS_SELECTED;
1996 LISTVIEW_SetItemState(hwnd,i,&item);
2002 * Sets a single group selection.
2005 * [I] HWND : window handle
2006 * [I] INT : item index
2011 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
2013 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2014 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2017 ZeroMemory(&item,sizeof(item));
2018 item.stateMask = LVIS_SELECTED;
2020 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2025 if (infoPtr->nSelectionMark == -1)
2027 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2031 nFirst = min(infoPtr->nSelectionMark, nItem);
2032 nLast = max(infoPtr->nSelectionMark, nItem);
2035 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2037 if ((i < nFirst) || (i > nLast))
2040 item.state = LVIS_SELECTED;
2041 LISTVIEW_SetItemState(hwnd,i,&item);
2049 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2050 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2051 rcSel.left = min(rcSelMark.left, rcItem.left);
2052 rcSel.top = min(rcSelMark.top, rcItem.top);
2053 rcSel.right = max(rcSelMark.right, rcItem.right);
2054 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2055 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2056 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2057 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2058 infoPtr->nSelectionMark,
2059 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2060 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2064 LISTVIEW_SetItemFocus(hwnd, nItem);
2069 * Manages the item focus.
2072 * [I] HWND : window handle
2073 * [I] INT : item index
2076 * TRUE : focused item changed
2077 * FALSE : focused item has NOT changed
2079 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2081 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2082 BOOL bResult = FALSE;
2085 if (infoPtr->nFocusedItem != nItem)
2087 if (infoPtr->nFocusedItem >= 0)
2089 INT oldFocus = infoPtr->nFocusedItem;
2091 infoPtr->nFocusedItem = -1;
2092 ZeroMemory(&lvItem, sizeof(lvItem));
2093 lvItem.stateMask = LVIS_FOCUSED;
2094 ListView_SetItemState(hwnd, oldFocus, &lvItem);
2098 lvItem.state = LVIS_FOCUSED;
2099 lvItem.stateMask = LVIS_FOCUSED;
2100 ListView_SetItemState(hwnd, nItem, &lvItem);
2102 infoPtr->nFocusedItem = nItem;
2103 ListView_EnsureVisible(hwnd, nItem, FALSE);
2111 * Sets a single selection.
2114 * [I] HWND : window handle
2115 * [I] INT : item index
2120 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2122 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2125 ZeroMemory(&lvItem, sizeof(lvItem));
2126 lvItem.stateMask = LVIS_FOCUSED;
2127 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2129 LISTVIEW_RemoveAllSelections(hwnd);
2131 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2132 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2133 ListView_SetItemState(hwnd, nItem, &lvItem);
2135 infoPtr->nFocusedItem = nItem;
2136 infoPtr->nSelectionMark = nItem;
2141 * Set selection(s) with keyboard.
2144 * [I] HWND : window handle
2145 * [I] INT : item index
2148 * SUCCESS : TRUE (needs to be repainted)
2149 * FAILURE : FALSE (nothing has changed)
2151 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2153 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2154 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2155 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2156 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2157 BOOL bResult = FALSE;
2159 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2161 if (lStyle & LVS_SINGLESEL)
2164 LISTVIEW_SetSelection(hwnd, nItem);
2165 ListView_EnsureVisible(hwnd, nItem, FALSE);
2172 LISTVIEW_SetGroupSelection(hwnd, nItem);
2176 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2181 LISTVIEW_SetSelection(hwnd, nItem);
2182 ListView_EnsureVisible(hwnd, nItem, FALSE);
2192 * Called when the mouse is being actively tracked and has hovered for a specified
2196 * [I] HWND : window handle
2197 * [I] wParam : key indicator
2198 * [I] lParam : mouse position
2201 * 0 if the message was processed, non-zero if there was an error
2204 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2205 * over the item for a certain period of time.
2208 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2210 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2213 pt.x = (INT)LOWORD(lParam);
2214 pt.y = (INT)HIWORD(lParam);
2216 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2217 /* select the item under the cursor */
2218 LISTVIEW_MouseSelection(hwnd, pt);
2226 * Called whenever WM_MOUSEMOVE is received.
2229 * [I] HWND : window handle
2230 * [I] wParam : key indicators
2231 * [I] lParam : cursor position
2234 * 0 if the message is processed, non-zero if there was an error
2236 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2238 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2239 TRACKMOUSEEVENT trackinfo;
2241 /* see if we are supposed to be tracking mouse hovering */
2242 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2243 /* fill in the trackinfo struct */
2244 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2245 trackinfo.dwFlags = TME_QUERY;
2246 trackinfo.hwndTrack = hwnd;
2247 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2249 /* see if we are already tracking this hwnd */
2250 _TrackMouseEvent(&trackinfo);
2252 if(!(trackinfo.dwFlags & TME_HOVER)) {
2253 trackinfo.dwFlags = TME_HOVER;
2255 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2256 _TrackMouseEvent(&trackinfo);
2265 * Selects an item based on coordinates.
2268 * [I] HWND : window handle
2269 * [I] POINT : mouse click ccordinates
2272 * SUCCESS : item index
2275 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2277 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2279 INT i,topindex,bottomindex;
2280 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2281 UINT uView = lStyle & LVS_TYPEMASK;
2283 topindex = ListView_GetTopIndex(hwnd);
2284 if (uView == LVS_REPORT)
2286 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2287 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2291 bottomindex = GETITEMCOUNT(infoPtr);
2294 for (i = topindex; i < bottomindex; i++)
2296 rcItem.left = LVIR_SELECTBOUNDS;
2297 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2299 if (PtInRect(&rcItem, pt) != FALSE)
2314 * [IO] HDPA : dynamic pointer array handle
2315 * [I] INT : column index (subitem index)
2321 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2323 BOOL bResult = TRUE;
2327 for (i = 0; i < hdpaItems->nItemCount; i++)
2329 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2330 if (hdpaSubItems != NULL)
2332 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2344 * Removes a subitem at a given position.
2347 * [IO] HDPA : dynamic pointer array handle
2348 * [I] INT : subitem index
2354 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2356 LISTVIEW_SUBITEM *lpSubItem;
2359 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2361 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2362 if (lpSubItem != NULL)
2364 if (lpSubItem->iSubItem == nSubItem)
2367 if (is_textW(lpSubItem->pszText))
2368 COMCTL32_Free(lpSubItem->pszText);
2371 COMCTL32_Free(lpSubItem);
2373 /* free dpa memory */
2374 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2377 else if (lpSubItem->iSubItem > nSubItem)
2387 * Compares the item information.
2390 * [I] LISTVIEW_ITEM *: destination item
2391 * [I] LPLVITEM : source item
2392 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2395 * SUCCCESS : TRUE (EQUAL)
2396 * FAILURE : FALSE (NOT EQUAL)
2398 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2402 if ((lpItem != NULL) && (lpLVItem != NULL))
2404 if (lpLVItem->mask & LVIF_STATE)
2406 if ((lpItem->state & lpLVItem->stateMask) !=
2407 (lpLVItem->state & lpLVItem->stateMask))
2408 uChanged |= LVIF_STATE;
2411 if (lpLVItem->mask & LVIF_IMAGE)
2413 if (lpItem->iImage != lpLVItem->iImage)
2414 uChanged |= LVIF_IMAGE;
2417 if (lpLVItem->mask & LVIF_PARAM)
2419 if (lpItem->lParam != lpLVItem->lParam)
2420 uChanged |= LVIF_PARAM;
2423 if (lpLVItem->mask & LVIF_INDENT)
2425 if (lpItem->iIndent != lpLVItem->iIndent)
2426 uChanged |= LVIF_INDENT;
2429 if (lpLVItem->mask & LVIF_TEXT)
2431 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2433 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2434 uChanged |= LVIF_TEXT;
2438 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2440 uChanged |= LVIF_TEXT;
2444 if (lpLVItem->pszText)
2446 if (lpItem->pszText)
2448 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2449 if (pszText && strcmpW(pszText, lpItem->pszText))
2450 uChanged |= LVIF_TEXT;
2451 textfreeT(pszText, isW);
2455 uChanged |= LVIF_TEXT;
2460 if (lpItem->pszText)
2461 uChanged |= LVIF_TEXT;
2472 * Initializes item attributes.
2475 * [I] HWND : window handle
2476 * [O] LISTVIEW_ITEM *: destination item
2477 * [I] LPLVITEM : source item
2478 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2484 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2485 LPLVITEMW lpLVItem, BOOL isW)
2487 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2488 BOOL bResult = FALSE;
2490 if ((lpItem != NULL) && (lpLVItem != NULL))
2494 if (lpLVItem->mask & LVIF_STATE)
2496 lpItem->state &= ~lpLVItem->stateMask;
2497 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2500 if (lpLVItem->mask & LVIF_IMAGE)
2501 lpItem->iImage = lpLVItem->iImage;
2503 if (lpLVItem->mask & LVIF_PARAM)
2504 lpItem->lParam = lpLVItem->lParam;
2506 if (lpLVItem->mask & LVIF_INDENT)
2507 lpItem->iIndent = lpLVItem->iIndent;
2509 if (lpLVItem->mask & LVIF_TEXT)
2511 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2513 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2516 if (is_textW(lpItem->pszText))
2517 COMCTL32_Free(lpItem->pszText);
2519 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2522 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2531 * Initializes subitem attributes.
2533 * NOTE: The documentation specifies that the operation fails if the user
2534 * tries to set the indent of a subitem.
2537 * [I] HWND : window handle
2538 * [O] LISTVIEW_SUBITEM *: destination subitem
2539 * [I] LPLVITEM : source subitem
2540 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2546 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2547 LPLVITEMW lpLVItem, BOOL isW)
2549 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2550 BOOL bResult = FALSE;
2552 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2553 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2555 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2557 if (!(lpLVItem->mask & LVIF_INDENT))
2561 lpSubItem->iSubItem = lpLVItem->iSubItem;
2563 if (lpLVItem->mask & LVIF_IMAGE)
2564 lpSubItem->iImage = lpLVItem->iImage;
2566 if (lpLVItem->mask & LVIF_TEXT)
2568 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2570 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2573 if (is_textW(lpSubItem->pszText))
2574 COMCTL32_Free(lpSubItem->pszText);
2576 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2579 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2589 * Adds a subitem at a given position (column index).
2592 * [I] HWND : window handle
2593 * [I] LPLVITEM : new subitem atttributes
2594 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2600 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2602 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2603 LISTVIEW_SUBITEM *lpSubItem = NULL;
2604 BOOL bResult = FALSE;
2606 INT nPosition, nItem;
2607 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2609 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2611 if (lStyle & LVS_OWNERDATA)
2614 if (lpLVItem != NULL)
2616 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2617 if (hdpaSubItems != NULL)
2619 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2620 if (lpSubItem != NULL)
2622 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2623 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2625 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2626 lpSubItem->iSubItem);
2627 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2628 if (nItem != -1) bResult = TRUE;
2634 /* cleanup if unsuccessful */
2635 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2642 * Finds the dpa insert position (array index).
2645 * [I] HWND : window handle
2646 * [I] INT : subitem index
2652 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2654 LISTVIEW_SUBITEM *lpSubItem;
2657 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2659 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2660 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2664 return hdpaSubItems->nItemCount;
2669 * Retrieves a listview subitem at a given position (column index).
2672 * [I] HWND : window handle
2673 * [I] INT : subitem index
2679 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2681 LISTVIEW_SUBITEM *lpSubItem;
2684 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2686 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2687 if (lpSubItem != NULL)
2689 if (lpSubItem->iSubItem == nSubItem)
2691 else if (lpSubItem->iSubItem > nSubItem)
2701 * Sets item attributes.
2704 * [I] HWND : window handle
2705 * [I] LPLVITEM : new item atttributes
2706 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2712 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2714 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2715 BOOL bResult = FALSE;
2717 LISTVIEW_ITEM *lpItem;
2719 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2721 UINT uView = lStyle & LVS_TYPEMASK;
2725 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2727 if (lStyle & LVS_OWNERDATA)
2729 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2733 ZeroMemory(&itm, sizeof(itm));
2734 itm.mask = LVIF_STATE | LVIF_PARAM;
2735 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2736 itm.iItem = lpLVItem->iItem;
2738 ListView_GetItemW(hwnd, &itm);
2741 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2742 nmlv.uNewState = lpLVItem->state;
2743 nmlv.uOldState = itm.state;
2744 nmlv.uChanged = LVIF_STATE;
2745 nmlv.lParam = itm.lParam;
2746 nmlv.iItem = lpLVItem->iItem;
2748 if ((itm.state & lpLVItem->stateMask) !=
2749 (lpLVItem->state & lpLVItem->stateMask))
2752 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent
2753 * by LVS_OWERNDATA list controls
2755 if (lpLVItem->stateMask & LVIS_FOCUSED)
2757 if (lpLVItem->state & LVIS_FOCUSED)
2758 infoPtr->nFocusedItem = lpLVItem->iItem;
2759 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2760 infoPtr->nFocusedItem = -1;
2762 if (lpLVItem->stateMask & LVIS_SELECTED)
2764 if (lpLVItem->state & LVIS_SELECTED)
2766 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2767 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2770 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2774 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2776 rcItem.left = LVIR_BOUNDS;
2777 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2778 if (!infoPtr->bIsDrawing)
2779 InvalidateRect(hwnd, &rcItem, TRUE);
2786 if (lpLVItem != NULL)
2788 if (lpLVItem->iSubItem == 0)
2790 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2791 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2793 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2796 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2797 nmlv.lParam = lpItem->lParam;
2798 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2801 if (uChanged & LVIF_STATE)
2803 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2804 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2806 if (nmlv.uNewState & LVIS_SELECTED)
2809 * This is redundant if called through SetSelection
2811 * however is required if the used directly calls SetItem
2812 * to set the selection.
2814 if (lStyle & LVS_SINGLESEL)
2815 LISTVIEW_RemoveAllSelections(hwnd);
2817 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2820 else if (lpLVItem->stateMask & LVIS_SELECTED)
2822 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2825 if (nmlv.uNewState & LVIS_FOCUSED)
2828 * This is a fun hoop to jump to try to catch if
2829 * the user is calling us directly to call focus or if
2830 * this function is being called as a result of a
2831 * SetItemFocus call.
2833 if (infoPtr->nFocusedItem >= 0)
2834 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2838 nmlv.uChanged = uChanged;
2839 nmlv.iItem = lpLVItem->iItem;
2840 nmlv.lParam = lpItem->lParam;
2841 /* send LVN_ITEMCHANGING notification */
2842 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2844 /* copy information */
2845 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2847 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2848 based on the width of the items text */
2849 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2851 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2853 if(item_width > infoPtr->nItemWidth)
2854 infoPtr->nItemWidth = item_width;
2857 /* send LVN_ITEMCHANGED notification */
2858 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2867 rcItem.left = LVIR_BOUNDS;
2868 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2869 if (!infoPtr->bIsDrawing)
2870 InvalidateRect(hwnd, &rcItem, TRUE);
2882 * Sets subitem attributes.
2885 * [I] HWND : window handle
2886 * [I] LPLVITEM : new subitem atttributes
2887 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2893 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2895 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2896 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2897 BOOL bResult = FALSE;
2899 LISTVIEW_SUBITEM *lpSubItem;
2902 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2904 if (lStyle & LVS_OWNERDATA)
2907 if (lpLVItem != NULL)
2909 if (lpLVItem->iSubItem > 0)
2911 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2912 if (hdpaSubItems != NULL)
2914 /* set subitem only if column is present */
2915 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2917 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2918 if (lpSubItem != NULL)
2919 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2921 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2923 rcItem.left = LVIR_BOUNDS;
2924 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2925 InvalidateRect(hwnd, &rcItem, FALSE);
2936 * Sets item attributes.
2939 * [I] HWND : window handle
2940 * [I] LPLVITEM : new item atttributes
2941 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2947 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2949 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2951 if (!lpLVItem || lpLVItem->iItem < 0 ||
2952 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2954 if (lpLVItem->iSubItem == 0)
2955 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2957 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2962 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2965 * [I] HWND : window handle
2970 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2972 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2973 UINT uView = lStyle & LVS_TYPEMASK;
2975 SCROLLINFO scrollInfo;
2977 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2978 scrollInfo.cbSize = sizeof(SCROLLINFO);
2979 scrollInfo.fMask = SIF_POS;
2981 if (uView == LVS_LIST)
2983 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
2984 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2986 else if (uView == LVS_REPORT)
2988 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
2989 nItem = scrollInfo.nPos;
3000 * [I] HWND : window handle
3001 * [I] HDC : device context handle
3002 * [I] INT : item index
3003 * [I] INT : subitem index
3004 * [I] RECT * : clipping rectangle
3009 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
3010 RECT rcItem, BOOL Selected)
3012 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3013 WCHAR szDispText[DISP_TEXT_SIZE];
3016 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3019 INT nLabelWidth = 0;
3021 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3024 /* get information needed for drawing the item */
3025 ZeroMemory(&lvItem, sizeof(lvItem));
3026 lvItem.mask = LVIF_TEXT;
3027 lvItem.iItem = nItem;
3028 lvItem.iSubItem = nSubItem;
3029 lvItem.cchTextMax = DISP_TEXT_SIZE;
3030 lvItem.pszText = szDispText;
3031 *lvItem.pszText = '\0';
3032 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3033 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3035 ZeroMemory(&lvColumn, sizeof(lvColumn));
3036 lvColumn.mask = LVCF_FMT;
3037 LISTVIEW_GetColumnT(hwnd, nSubItem, &lvColumn, TRUE);
3038 textLeft = rcItem.left;
3039 if (lvColumn.fmt != LVCFMT_LEFT)
3041 if ((nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE)))
3043 if (lvColumn.fmt == LVCFMT_RIGHT)
3044 textLeft = rcItem.right - nLabelWidth;
3046 textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
3051 /* redraw the background of the item */
3053 if(infoPtr->nColumnCount == (nSubItem + 1))
3054 rcTemp.right = infoPtr->rcList.right;
3056 rcTemp.right += WIDTH_PADDING;
3058 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3060 /* set item colors */
3061 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3063 if (infoPtr->bFocus)
3065 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3066 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3070 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3071 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3076 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3078 SetBkMode(hdc, TRANSPARENT);
3079 textoutOptions &= ~ETO_OPAQUE;
3083 SetBkMode(hdc, OPAQUE);
3084 SetBkColor(hdc, infoPtr->clrTextBk);
3087 SetTextColor(hdc, infoPtr->clrText);
3090 ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
3091 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3095 /* fill in the gap */
3097 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3099 CopyRect(&rec,&rcItem);
3100 rec.left = rec.right;
3101 rec.right = rec.left+REPORT_MARGINX;
3102 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3103 &rec, NULL, 0, NULL);
3105 CopyRect(&rec,&rcItem);
3106 rec.right = rec.left;
3107 rec.left = rec.left - REPORT_MARGINX;
3108 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3109 &rec, NULL, 0, NULL);
3119 * [I] HWND : window handle
3120 * [I] HDC : device context handle
3121 * [I] INT : item index
3122 * [I] RECT * : clipping rectangle
3127 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3129 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3130 WCHAR szDispText[DISP_TEXT_SIZE];
3135 DWORD dwTextColor,dwTextX;
3136 BOOL bImage = FALSE;
3138 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3141 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3144 /* get information needed for drawing the item */
3145 ZeroMemory(&lvItem, sizeof(lvItem));
3146 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3147 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3148 lvItem.iItem = nItem;
3149 lvItem.iSubItem = 0;
3150 lvItem.cchTextMax = DISP_TEXT_SIZE;
3151 lvItem.pszText = szDispText;
3152 *lvItem.pszText = '\0';
3153 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3154 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3156 /* redraw the background of the item */
3158 if(infoPtr->nColumnCount == (nItem + 1))
3159 rcTemp.right = infoPtr->rcList.right;
3161 rcTemp.right+=WIDTH_PADDING;
3163 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3166 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3168 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3171 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3175 if (infoPtr->himlState != NULL)
3177 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3178 if (uStateImage > 0)
3180 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3181 rcItem.top, ILD_NORMAL);
3184 rcItem.left += infoPtr->iconSize.cx;
3186 SuggestedFocus->left += infoPtr->iconSize.cx;
3191 if (infoPtr->himlSmall != NULL)
3193 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3196 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3197 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3198 rcItem.top, ILD_SELECTED);
3200 else if (lvItem.iImage>=0)
3202 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3203 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3204 rcItem.top, ILD_NORMAL);
3207 rcItem.left += infoPtr->iconSize.cx;
3210 SuggestedFocus->left += infoPtr->iconSize.cx;
3214 /* Don't bother painting item being edited */
3215 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3218 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3220 /* set item colors */
3221 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3222 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3223 /* set raster mode */
3224 nMixMode = SetROP2(hdc, R2_XORPEN);
3226 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3227 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3229 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3230 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3231 /* set raster mode */
3232 nMixMode = SetROP2(hdc, R2_COPYPEN);
3236 /* set item colors */
3237 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3239 dwBkColor = GetBkColor(hdc);
3240 iBkMode = SetBkMode(hdc, TRANSPARENT);
3241 textoutOptions &= ~ETO_OPAQUE;
3245 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3246 iBkMode = SetBkMode(hdc, OPAQUE);
3249 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3250 /* set raster mode */
3251 nMixMode = SetROP2(hdc, R2_COPYPEN);
3254 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3255 if (rcItem.left + nLabelWidth < rcItem.right)
3258 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3260 rcItem.right += IMAGE_PADDING;
3264 dwTextX = rcItem.left + 1;
3266 dwTextX += IMAGE_PADDING;
3269 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3270 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3272 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3274 /* fill in the gap */
3276 CopyRect(&rec,&rcItem);
3277 rec.left = rec.right;
3278 rec.right = rec.left+REPORT_MARGINX;
3279 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3280 &rec, NULL, 0, NULL);
3284 CopyRect(SuggestedFocus,&rcItem);
3288 SetROP2(hdc, R2_COPYPEN);
3289 SetBkColor(hdc, dwBkColor);
3290 SetTextColor(hdc, dwTextColor);
3292 SetBkMode(hdc, iBkMode);
3298 * Draws an item when in large icon display mode.
3301 * [I] HWND : window handle
3302 * [I] HDC : device context handle
3303 * [I] INT : item index
3304 * [I] RECT : clipping rectangle
3305 * [O] RECT * : The text rectangle about which to draw the focus
3310 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3311 RECT *SuggestedFocus)
3313 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3314 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3316 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3318 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3321 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3322 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3324 /* get information needed for drawing the item */
3325 ZeroMemory(&lvItem, sizeof(lvItem));
3326 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3327 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3328 lvItem.iItem = nItem;
3329 lvItem.iSubItem = 0;
3330 lvItem.cchTextMax = DISP_TEXT_SIZE;
3331 lvItem.pszText = szDispText;
3332 *lvItem.pszText = '\0';
3333 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3334 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3336 /* redraw the background of the item */
3338 if(infoPtr->nColumnCount == (nItem + 1))
3339 rcTemp.right = infoPtr->rcList.right;
3341 rcTemp.right+=WIDTH_PADDING;
3342 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3344 TRACE("background rect (%d,%d)-(%d,%d)\n",
3345 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3347 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3350 /* Figure out text colours etc. depending on state
3351 * At least the following states exist; there may be more.
3352 * Many items may be selected
3353 * At most one item may have the focus
3354 * The application may not actually be active currently
3355 * 1. The item is not selected in any way
3356 * 2. The cursor is flying over the icon or text and the text is being
3357 * expanded because it is not fully displayed currently.
3358 * 3. The item is selected and is focussed, i.e. the user has not clicked
3359 * in the blank area of the window, and the window (or application?)
3360 * still has the focus.
3361 * 4. As 3 except that a different window has the focus
3362 * 5. The item is the selected item of all the items, but the user has
3363 * clicked somewhere else on the window.
3364 * Only a few of these are handled currently. In particular 2 is not yet
3365 * handled since we do not support the functionality currently (or at least
3366 * we didn't when I wrote this)
3369 if (lvItem.state & LVIS_SELECTED)
3371 /* set item colors */
3372 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3373 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3374 SetBkMode (hdc, OPAQUE);
3375 /* set raster mode */
3376 SetROP2(hdc, R2_XORPEN);
3377 /* When exactly is it in XOR? while being dragged? */
3381 /* set item colors */
3382 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3384 SetBkMode(hdc, TRANSPARENT);
3388 SetBkMode(hdc, OPAQUE);
3389 SetBkColor(hdc, infoPtr->clrTextBk);
3392 SetTextColor(hdc, infoPtr->clrText);
3393 /* set raster mode */
3394 SetROP2(hdc, R2_COPYPEN);
3397 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3398 * wrapping and long words split.
3399 * In cases 1 and 4 only a portion of the text is displayed with word
3400 * wrapping and both word and end ellipsis. (I don't yet know about path
3403 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3406 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3409 if (infoPtr->himlNormal != NULL)
3411 if (lvItem.iImage >= 0)
3413 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3415 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3419 /* Draw the text below the icon */
3421 /* Don't bother painting item being edited */
3422 if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
3423 !lstrlenW(lvItem.pszText))
3425 SetRectEmpty(SuggestedFocus);
3429 /* Since rcItem.left is left point of icon, compute left point of item box */
3430 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3431 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3432 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3433 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3434 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3435 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3436 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3437 infoPtr->rcList.left, infoPtr->rcList.top,
3438 infoPtr->rcList.right, infoPtr->rcList.bottom,
3439 infoPtr->rcView.left, infoPtr->rcView.top,
3440 infoPtr->rcView.right, infoPtr->rcView.bottom);
3442 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3443 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3448 /* I am sure of most of the uFormat values. However I am not sure about
3449 * whether we need or do not need the following:
3450 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3451 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3452 * We certainly do not need
3453 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3454 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3457 /* If the text is being drawn without clipping (i.e. the full text) then we
3458 * need to jump through a few hoops to ensure that it all gets displayed and
3459 * that the background is complete
3461 if (uFormat & DT_NOCLIP)
3464 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3465 int dx, dy, old_wid, new_wid;
3466 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3467 /* Microsoft, in their great wisdom, have decided that the rectangle
3468 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3469 * not the location. So we have to do the centring ourselves (and take
3470 * responsibility for agreeing off-by-one consistency with them).
3472 old_wid = rcItem.right-rcItem.left;
3473 new_wid = rcBack.right - rcBack.left;
3474 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3475 dy = rcBack.top - rcItem.top;
3476 OffsetRect (&rcItem, dx, dy);
3477 FillRect(hdc, &rcItem, hBrush);
3478 DeleteObject(hBrush);
3480 /* else ? What if we are losing the focus? will we not get a complete
3483 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3485 CopyRect(SuggestedFocus, &rcItem);
3490 * Draws listview items when in report display mode.
3493 * [I] HWND : window handle
3494 * [I] HDC : device context handle
3499 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3501 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3502 SCROLLINFO scrollInfo;
3503 INT nDrawPosY = infoPtr->rcList.top;
3505 RECT rcItem, rcTemp;
3510 DWORD cditemmode = CDRF_DODEFAULT;
3511 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3514 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3515 scrollInfo.cbSize = sizeof(SCROLLINFO);
3516 scrollInfo.fMask = SIF_POS;
3518 nItem = ListView_GetTopIndex(hwnd);
3520 /* add 1 for displaying a partial item at the bottom */
3521 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3522 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3524 /* send cache hint notification */
3525 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3529 nmlv.hdr.hwndFrom = hwnd;
3530 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3531 nmlv.hdr.code = LVN_ODCACHEHINT;
3535 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3539 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3540 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3541 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3543 /* clear the background of any part of the control that doesn't contain items */
3544 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3545 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3547 /* nothing to draw */
3548 if(GETITEMCOUNT(infoPtr) == 0)
3551 /* Get scroll bar info once before loop */
3552 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3553 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3555 for (; nItem < nLast; nItem++)
3557 RECT SuggestedFocusRect;
3560 if (lStyle & LVS_OWNERDRAWFIXED)
3562 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3567 TRACE("Owner Drawn\n");
3568 dis.CtlType = ODT_LISTVIEW;
3571 dis.itemAction = ODA_DRAWENTIRE;
3574 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3575 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3577 dis.hwndItem = hwnd;
3580 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3582 dis.rcItem.left = -scrollOffset;
3583 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3584 dis.rcItem.top = nDrawPosY;
3585 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3587 ZeroMemory(&item,sizeof(item));
3589 item.mask = LVIF_PARAM;
3590 ListView_GetItemW(hwnd, &item);
3592 dis.itemData = item.lParam;
3594 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3596 nDrawPosY += infoPtr->nItemHeight;
3605 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3606 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3608 ir.left += REPORT_MARGINX;
3609 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3611 ir.bottom = ir.top + infoPtr->nItemHeight;
3613 CopyRect(&SuggestedFocusRect,&ir);
3616 for (j = 0; j < nColumnCount; j++)
3618 if (cdmode & CDRF_NOTIFYITEMDRAW)
3619 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3621 if (cditemmode & CDRF_SKIPDEFAULT)
3624 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3626 rcItem.left += REPORT_MARGINX;
3627 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3628 rcItem.top = nDrawPosY;
3629 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3631 /* Offset the Scroll Bar Pos */
3632 rcItem.left -= scrollOffset;
3633 rcItem.right -= scrollOffset;
3637 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3638 &SuggestedFocusRect);
3642 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3645 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3646 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3647 CDDS_ITEMPOSTPAINT);
3652 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3655 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3656 rop = SetROP2(hdc, R2_XORPEN);
3658 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3659 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3662 SetROP2(hdc, R2_COPYPEN);
3664 nDrawPosY += infoPtr->nItemHeight;
3670 * Retrieves the number of items that can fit vertically in the client area.
3673 * [I] HWND : window handle
3676 * Number of items per row.
3678 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3680 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3681 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3682 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3683 INT nCountPerRow = 1;
3687 if (uView != LVS_REPORT)
3689 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3690 if (nCountPerRow == 0) nCountPerRow = 1;
3694 return nCountPerRow;
3699 * Retrieves the number of items that can fit horizontally in the client
3703 * [I] HWND : window handle
3706 * Number of items per column.
3708 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3710 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3711 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3712 INT nCountPerColumn = 1;
3714 if (nListHeight > 0)
3716 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3717 if (nCountPerColumn == 0) nCountPerColumn = 1;
3720 return nCountPerColumn;
3725 * Retrieves the number of columns needed to display all the items when in
3726 * list display mode.
3729 * [I] HWND : window handle
3732 * Number of columns.
3734 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3736 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3737 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3738 INT nColumnCount = 0;
3740 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3742 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3743 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3746 return nColumnCount;
3752 * Draws listview items when in list display mode.
3755 * [I] HWND : window handle
3756 * [I] HDC : device context handle
3761 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3763 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3764 RECT rcItem, FocusRect, rcTemp;
3768 INT nCountPerColumn;
3769 INT nItemWidth = infoPtr->nItemWidth;
3770 INT nItemHeight = infoPtr->nItemHeight;
3771 DWORD cditemmode = CDRF_DODEFAULT;
3773 /* get number of fully visible columns */
3774 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3775 infoPtr->nColumnCount = nColumnCount;
3776 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3777 nItem = ListView_GetTopIndex(hwnd);
3779 /* paint the background of the control that doesn't contain any items */
3780 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3781 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3783 /* nothing to draw, return here */
3784 if(GETITEMCOUNT(infoPtr) == 0)
3787 for (i = 0; i < nColumnCount; i++)
3789 for (j = 0; j < nCountPerColumn; j++, nItem++)
3791 if (nItem >= GETITEMCOUNT(infoPtr))
3794 if (cdmode & CDRF_NOTIFYITEMDRAW)
3795 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3797 if (cditemmode & CDRF_SKIPDEFAULT)
3800 rcItem.top = j * nItemHeight;
3801 rcItem.left = i * nItemWidth;
3802 rcItem.bottom = rcItem.top + nItemHeight;
3803 rcItem.right = rcItem.left + nItemWidth;
3804 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3808 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3809 Rectangle(hdc, FocusRect.left, FocusRect.top,
3810 FocusRect.right,FocusRect.bottom);
3812 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3813 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3814 CDDS_ITEMPOSTPAINT);
3822 * Draws listview items when in icon or small icon display mode.
3825 * [I] HWND : window handle
3826 * [I] HDC : device context handle
3831 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3833 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3836 RECT rcItem, SuggestedFocus, rcTemp;
3838 DWORD cditemmode = CDRF_DODEFAULT;
3840 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3841 /* DrawItem from erasing the incorrect background area */
3843 /* paint the background of the control that doesn't contain any items */
3844 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3845 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3847 /* nothing to draw, return here */
3848 if(GETITEMCOUNT(infoPtr) == 0)
3851 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3852 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3854 if (cdmode & CDRF_NOTIFYITEMDRAW)
3855 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3857 if (cditemmode & CDRF_SKIPDEFAULT)
3860 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3861 ptPosition.x += ptOrigin.x;
3862 ptPosition.y += ptOrigin.y;
3864 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3866 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3868 if (ptPosition.y < infoPtr->rcList.bottom)
3870 if (ptPosition.x < infoPtr->rcList.right)
3872 rcItem.top = ptPosition.y;
3873 rcItem.left = ptPosition.x;
3874 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3875 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3877 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3879 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3883 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3884 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3885 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3886 SuggestedFocus.right,SuggestedFocus.bottom);
3891 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3892 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3893 CDDS_ITEMPOSTPAINT);
3899 * Draws listview items.
3902 * [I] HWND : window handle
3903 * [I] HDC : device context handle
3908 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3910 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3911 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3917 infoPtr->bIsDrawing = TRUE;
3918 LISTVIEW_DumpListview (infoPtr, __LINE__);
3920 GetClientRect(hwnd, &rect);
3921 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3923 if (cdmode == CDRF_SKIPDEFAULT) return;
3926 hOldFont = SelectObject(hdc, infoPtr->hFont);
3928 /* select the dotted pen (for drawing the focus box) */
3929 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3930 hOldPen = SelectObject(hdc, hPen);
3932 /* select transparent brush (for drawing the focus box) */
3933 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3935 if (uView == LVS_LIST)
3936 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3937 else if (uView == LVS_REPORT)
3938 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3939 else if (uView == LVS_SMALLICON)
3940 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3941 else if (uView == LVS_ICON)
3942 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3944 /* unselect objects */
3945 SelectObject(hdc, hOldFont);
3946 SelectObject(hdc, hOldPen);
3951 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3952 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3954 infoPtr->bIsDrawing = FALSE;
3960 * Calculates the approximate width and height of a given number of items.
3963 * [I] HWND : window handle
3964 * [I] INT : number of items
3969 * Returns a DWORD. The width in the low word and the height in high word.
3971 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3972 WORD wWidth, WORD wHeight)
3974 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3975 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3976 INT nItemCountPerColumn = 1;
3977 INT nColumnCount = 0;
3978 DWORD dwViewRect = 0;
3980 if (nItemCount == -1)
3981 nItemCount = GETITEMCOUNT(infoPtr);
3983 if (uView == LVS_LIST)
3985 if (wHeight == 0xFFFF)
3987 /* use current height */
3988 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3991 if (wHeight < infoPtr->nItemHeight)
3992 wHeight = infoPtr->nItemHeight;
3996 if (infoPtr->nItemHeight > 0)
3998 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3999 if (nItemCountPerColumn == 0)
4000 nItemCountPerColumn = 1;
4002 if (nItemCount % nItemCountPerColumn != 0)
4003 nColumnCount = nItemCount / nItemCountPerColumn;
4005 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4009 /* Microsoft padding magic */
4010 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4011 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4013 dwViewRect = MAKELONG(wWidth, wHeight);
4015 else if (uView == LVS_REPORT)
4016 FIXME("uView == LVS_REPORT: not implemented\n");
4017 else if (uView == LVS_SMALLICON)
4018 FIXME("uView == LVS_SMALLICON: not implemented\n");
4019 else if (uView == LVS_ICON)
4020 FIXME("uView == LVS_ICON: not implemented\n");
4027 * Arranges listview items in icon display mode.
4030 * [I] HWND : window handle
4031 * [I] INT : alignment code
4037 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
4039 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4040 BOOL bResult = FALSE;
4042 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4047 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4050 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4053 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4055 case LVA_SNAPTOGRID:
4056 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4064 /* << LISTVIEW_CreateDragImage >> */
4069 * Removes all listview items and subitems.
4072 * [I] HWND : window handle
4078 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4080 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4081 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4082 UINT uView = lStyle & LVS_TYPEMASK;
4083 LISTVIEW_ITEM *lpItem;
4084 LISTVIEW_SUBITEM *lpSubItem;
4087 BOOL bResult = FALSE;
4090 TRACE("(hwnd=%x,)\n", hwnd);
4092 LISTVIEW_RemoveAllSelections(hwnd);
4093 infoPtr->nSelectionMark=-1;
4094 infoPtr->nFocusedItem=-1;
4095 /* But we are supposed to leave nHotItem as is! */
4097 if (lStyle & LVS_OWNERDATA)
4099 infoPtr->hdpaItems->nItemCount = 0;
4100 InvalidateRect(hwnd, NULL, TRUE);
4104 if (GETITEMCOUNT(infoPtr) > 0)
4108 /* send LVN_DELETEALLITEMS notification */
4109 /* verify if subsequent LVN_DELETEITEM notifications should be
4111 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4113 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4115 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4117 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4118 if (hdpaSubItems != NULL)
4120 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4122 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4123 if (lpSubItem != NULL)
4125 /* free subitem string */
4126 if (is_textW(lpSubItem->pszText))
4127 COMCTL32_Free(lpSubItem->pszText);
4130 COMCTL32_Free(lpSubItem);
4134 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4139 /* send LVN_DELETEITEM notification */
4141 nmlv.lParam = lpItem->lParam;
4142 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4145 /* free item string */
4146 if (is_textW(lpItem->pszText))
4147 COMCTL32_Free(lpItem->pszText);
4150 COMCTL32_Free(lpItem);
4153 DPA_Destroy(hdpaSubItems);
4157 /* reinitialize listview memory */
4158 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4160 /* align items (set position of each item) */
4161 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4163 if (lStyle & LVS_ALIGNLEFT)
4165 LISTVIEW_AlignLeft(hwnd);
4169 LISTVIEW_AlignTop(hwnd);
4173 LISTVIEW_UpdateScroll(hwnd);
4175 /* invalidate client area (optimization needed) */
4176 InvalidateRect(hwnd, NULL, TRUE);
4184 * Removes a column from the listview control.
4187 * [I] HWND : window handle
4188 * [I] INT : column index
4194 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4196 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4197 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4198 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4199 BOOL bResult = FALSE;
4201 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4204 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4206 /* Need to reset the item width when deleting a column */
4207 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4209 /* reset scroll parameters */
4210 if (uView == LVS_REPORT)
4212 /* update scrollbar(s) */
4213 LISTVIEW_UpdateScroll(hwnd);
4215 /* refresh client area */
4216 InvalidateRect(hwnd, NULL, FALSE);
4225 * Removes an item from the listview control.
4228 * [I] HWND : window handle
4229 * [I] INT : item index
4235 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4237 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4238 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4239 UINT uView = lStyle & LVS_TYPEMASK;
4240 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4242 BOOL bResult = FALSE;
4244 LISTVIEW_ITEM *lpItem;
4245 LISTVIEW_SUBITEM *lpSubItem;
4249 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4252 /* First, send LVN_DELETEITEM notification. */
4253 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4254 nmlv.hdr.hwndFrom = hwnd;
4255 nmlv.hdr.idFrom = lCtrlId;
4256 nmlv.hdr.code = LVN_DELETEITEM;
4258 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4262 /* remove it from the selection range */
4263 ZeroMemory(&item,sizeof(item));
4264 item.stateMask = LVIS_SELECTED;
4265 LISTVIEW_SetItemState(hwnd,nItem,&item);
4267 if (lStyle & LVS_OWNERDATA)
4269 infoPtr->hdpaItems->nItemCount --;
4270 InvalidateRect(hwnd, NULL, TRUE);
4274 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4276 /* initialize memory */
4277 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4279 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4280 if (hdpaSubItems != NULL)
4282 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4284 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4285 if (lpSubItem != NULL)
4287 /* free item string */
4288 if (is_textW(lpSubItem->pszText))
4289 COMCTL32_Free(lpSubItem->pszText);
4292 COMCTL32_Free(lpSubItem);
4296 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4299 /* free item string */
4300 if (is_textW(lpItem->pszText))
4301 COMCTL32_Free(lpItem->pszText);
4304 COMCTL32_Free(lpItem);
4307 bResult = DPA_Destroy(hdpaSubItems);
4310 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4312 /* align items (set position of each item) */
4313 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4315 if (lStyle & LVS_ALIGNLEFT)
4316 LISTVIEW_AlignLeft(hwnd);
4318 LISTVIEW_AlignTop(hwnd);
4321 LISTVIEW_UpdateScroll(hwnd);
4323 /* refresh client area */
4324 InvalidateRect(hwnd, NULL, TRUE);
4333 * Return edit control handle of current edit label
4336 * [I] HWND : window handle
4342 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4344 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4345 return infoPtr->hwndEdit;
4351 * Callback implementation for editlabel control
4354 * [I] HWND : window handle
4355 * [I] LPSTR : modified text
4356 * [I] DWORD : item index
4357 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4363 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4365 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4366 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4367 NMLVDISPINFOW dispInfo;
4368 LISTVIEW_ITEM *lpItem;
4370 LISTVIEW_ITEM lvItemRef;
4372 BOOL bResult = TRUE;
4374 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4376 if (!(lStyle & LVS_OWNERDATA))
4378 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4381 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4386 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4387 ZeroMemory(&item,sizeof(item));
4390 item.mask = LVIF_PARAM | LVIF_STATE;
4391 ListView_GetItemW(hwnd, &item);
4392 lvItemRef.state = item.state;
4393 lvItemRef.iImage = item.iImage;
4394 lvItemRef.lParam = item.lParam;
4395 lpItem = &lvItemRef;
4398 ZeroMemory(&dispInfo, sizeof(dispInfo));
4399 dispInfo.item.mask = 0;
4400 dispInfo.item.iItem = nItem;
4401 dispInfo.item.state = lpItem->state;
4402 dispInfo.item.stateMask = 0;
4403 dispInfo.item.pszText = pszText;
4404 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4405 dispInfo.item.iImage = lpItem->iImage;
4406 dispInfo.item.lParam = lpItem->lParam;
4407 infoPtr->hwndEdit = 0;
4409 /* Do we need to update the Item Text */
4410 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4411 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4412 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4419 * Callback implementation for editlabel control
4422 * [I] HWND : window handle
4423 * [I] LPSTR : modified text
4424 * [I] DWORD : item index
4430 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4432 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4437 * Callback implementation for editlabel control
4440 * [I] HWND : window handle
4441 * [I] LPSTR : modified text
4442 * [I] DWORD : item index
4448 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4450 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4455 * Begin in place editing of specified list view item
4458 * [I] HWND : window handle
4459 * [I] INT : item index
4460 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4466 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4468 NMLVDISPINFOW dispInfo;
4470 LISTVIEW_ITEM *lpItem;
4472 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4473 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4475 WCHAR szDispText[DISP_TEXT_SIZE];
4477 LISTVIEW_ITEM lvItemRef;
4478 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4480 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4483 /* Is the EditBox still there, if so remove it */
4484 if(infoPtr->hwndEdit != 0)
4487 LISTVIEW_SetSelection(hwnd, nItem);
4488 LISTVIEW_SetItemFocus(hwnd, nItem);
4490 if (!(lStyle & LVS_OWNERDATA))
4492 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4495 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4501 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4502 ZeroMemory(&item, sizeof(item));
4505 item.mask = LVIF_PARAM | LVIF_STATE;
4506 ListView_GetItemW(hwnd, &item);
4507 lvItemRef.iImage = item.iImage;
4508 lvItemRef.state = item.state;
4509 lvItemRef.lParam = item.lParam;
4510 lpItem = &lvItemRef;
4513 /* get information needed for drawing the item */
4514 ZeroMemory(&lvItem, sizeof(lvItem));
4515 lvItem.mask = LVIF_TEXT;
4516 lvItem.iItem = nItem;
4517 lvItem.iSubItem = 0;
4518 lvItem.cchTextMax = DISP_TEXT_SIZE;
4519 lvItem.pszText = szDispText;
4520 *lvItem.pszText = '\0';
4521 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4523 ZeroMemory(&dispInfo, sizeof(dispInfo));
4524 dispInfo.item.mask = 0;
4525 dispInfo.item.iItem = nItem;
4526 dispInfo.item.state = lpItem->state;
4527 dispInfo.item.stateMask = 0;
4528 dispInfo.item.pszText = lvItem.pszText;
4529 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4530 dispInfo.item.iImage = lpItem->iImage;
4531 dispInfo.item.lParam = lpItem->lParam;
4533 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4536 rect.left = LVIR_LABEL;
4537 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4540 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4541 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4542 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4546 infoPtr->hwndEdit = hedit;
4548 SendMessageW(hedit, EM_SETSEL, 0, -1);
4556 * Ensures the specified item is visible, scrolling into view if necessary.
4559 * [I] HWND : window handle
4560 * [I] INT : item index
4561 * [I] BOOL : partially or entirely visible
4567 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4569 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4570 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4571 INT nScrollPosHeight = 0;
4572 INT nScrollPosWidth = 0;
4573 SCROLLINFO scrollInfo;
4575 BOOL bRedraw = FALSE;
4577 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4578 scrollInfo.cbSize = sizeof(SCROLLINFO);
4579 scrollInfo.fMask = SIF_POS;
4581 /* ALWAYS bPartial == FALSE, FOR NOW! */
4583 rcItem.left = LVIR_BOUNDS;
4584 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4586 if (rcItem.left < infoPtr->rcList.left)
4588 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4592 if (uView == LVS_LIST)
4594 nScrollPosWidth = infoPtr->nItemWidth;
4595 rcItem.left += infoPtr->rcList.left;
4597 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4599 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4600 rcItem.left += infoPtr->rcList.left;
4603 /* When in LVS_REPORT view, the scroll position should
4605 if (nScrollPosWidth != 0)
4607 if (rcItem.left % nScrollPosWidth == 0)
4608 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4610 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4612 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4616 else if (rcItem.right > infoPtr->rcList.right)
4618 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4622 if (uView == LVS_LIST)
4624 rcItem.right -= infoPtr->rcList.right;
4625 nScrollPosWidth = infoPtr->nItemWidth;
4627 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4629 rcItem.right -= infoPtr->rcList.right;
4630 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4633 /* When in LVS_REPORT view, the scroll position should
4635 if (nScrollPosWidth != 0)
4637 if (rcItem.right % nScrollPosWidth == 0)
4638 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4640 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4642 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4647 if (rcItem.top < infoPtr->rcList.top)
4651 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4653 if (uView == LVS_REPORT)
4655 rcItem.top -= infoPtr->rcList.top;
4656 nScrollPosHeight = infoPtr->nItemHeight;
4658 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4660 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4661 rcItem.top += infoPtr->rcList.top;
4664 if (rcItem.top % nScrollPosHeight == 0)
4665 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4667 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4669 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4672 else if (rcItem.bottom > infoPtr->rcList.bottom)
4676 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4678 if (uView == LVS_REPORT)
4680 rcItem.bottom -= infoPtr->rcList.bottom;
4681 nScrollPosHeight = infoPtr->nItemHeight;
4683 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4685 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4686 rcItem.bottom -= infoPtr->rcList.bottom;
4689 if (rcItem.bottom % nScrollPosHeight == 0)
4690 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4692 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4694 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4700 InvalidateRect(hwnd,NULL,TRUE);
4706 * Retrieves the nearest item, given a position and a direction.
4709 * [I] HWND : window handle
4710 * [I] POINT : start position
4711 * [I] UINT : direction
4714 * Item index if successdful, -1 otherwise.
4716 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4718 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4723 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4724 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4725 ((vkDirection == VK_UP) ? "VK_UP" :
4726 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4728 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4730 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4731 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4732 lvIntHit.ht.pt.x += pt.x;
4733 lvIntHit.ht.pt.y += pt.y;
4735 if (vkDirection == VK_DOWN)
4736 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4737 else if (vkDirection == VK_UP)
4738 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4739 else if (vkDirection == VK_LEFT)
4740 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4741 else if (vkDirection == VK_RIGHT)
4742 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4744 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4748 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4749 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4758 * Searches for an item with specific characteristics.
4761 * [I] hwnd : window handle
4762 * [I] nStart : base item index
4763 * [I] lpFindInfo : item information to look for
4766 * SUCCESS : index of item
4769 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4770 LPLVFINDINFOW lpFindInfo)
4772 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4774 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4778 INT nLast = GETITEMCOUNT(infoPtr);
4780 if ((nItem >= -1) && (lpFindInfo != NULL))
4782 ZeroMemory(&lvItem, sizeof(lvItem));
4784 if (lpFindInfo->flags & LVFI_PARAM)
4786 lvItem.mask |= LVIF_PARAM;
4789 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4791 lvItem.mask |= LVIF_TEXT;
4792 lvItem.pszText = szDispText;
4793 lvItem.cchTextMax = DISP_TEXT_SIZE;
4796 if (lpFindInfo->flags & LVFI_WRAP)
4799 if (lpFindInfo->flags & LVFI_NEARESTXY)
4801 ptItem.x = lpFindInfo->pt.x;
4802 ptItem.y = lpFindInfo->pt.y;
4807 while (nItem < nLast)
4809 if (lpFindInfo->flags & LVFI_NEARESTXY)
4811 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4812 lpFindInfo->vkDirection);
4815 /* get position of the new item index */
4816 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4827 lvItem.iItem = nItem;
4828 lvItem.iSubItem = 0;
4829 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4831 if (lvItem.mask & LVIF_TEXT)
4833 if (lpFindInfo->flags & LVFI_PARTIAL)
4835 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4840 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4845 if (lvItem.mask & LVIF_PARAM)
4847 if (lpFindInfo->lParam != lvItem.lParam)
4873 * Searches for an item with specific characteristics.
4876 * [I] hwnd : window handle
4877 * [I] nStart : base item index
4878 * [I] lpFindInfo : item information to look for
4881 * SUCCESS : index of item
4884 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4885 LPLVFINDINFOA lpFindInfo)
4887 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4891 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4892 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4893 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4894 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4900 * Retrieves the background color of the listview control.
4903 * [I] HWND : window handle
4906 * COLORREF associated with the background.
4908 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4910 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4912 return infoPtr->clrBk;
4917 * Retrieves the background image of the listview control.
4920 * [I] HWND : window handle
4921 * [O] LPLVMKBIMAGE : background image attributes
4927 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4929 /* FIXME (listview, "empty stub!\n"); */
4935 * Retrieves the callback mask.
4938 * [I] HWND : window handle
4943 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4945 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4947 return infoPtr->uCallbackMask;
4952 * Retrieves column attributes.
4955 * [I] HWND : window handle
4956 * [I] INT : column index
4957 * [IO] LPLVCOLUMNW : column information
4958 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4959 * otherwise it is in fact a LPLVCOLUMNA
4965 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4967 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4969 BOOL bResult = FALSE;
4971 if (lpColumn != NULL)
4974 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
4975 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
4977 /* initialize memory */
4978 ZeroMemory(&hdi, sizeof(hdi));
4980 if (lpColumn->mask & LVCF_FMT)
4981 hdi.mask |= HDI_FORMAT;
4983 if (lpColumn->mask & LVCF_WIDTH)
4984 hdi.mask |= HDI_WIDTH;
4986 if (lpColumn->mask & LVCF_TEXT)
4988 hdi.mask |= HDI_TEXT;
4989 hdi.cchTextMax = lpColumn->cchTextMax;
4990 hdi.pszText = lpColumn->pszText;
4993 if (lpColumn->mask & LVCF_IMAGE)
4994 hdi.mask |= HDI_IMAGE;
4996 if (lpColumn->mask & LVCF_ORDER)
4997 hdi.mask |= HDI_ORDER;
5000 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
5002 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
5004 if (bResult != FALSE)
5006 if (lpColumn->mask & LVCF_FMT)
5010 if (hdi.fmt & HDF_LEFT)
5011 lpColumn->fmt |= LVCFMT_LEFT;
5012 else if (hdi.fmt & HDF_RIGHT)
5013 lpColumn->fmt |= LVCFMT_RIGHT;
5014 else if (hdi.fmt & HDF_CENTER)
5015 lpColumn->fmt |= LVCFMT_CENTER;
5017 if (hdi.fmt & HDF_IMAGE)
5018 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
5020 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
5021 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
5024 if (lpColumn->mask & LVCF_WIDTH)
5025 lpColumn->cx = hdi.cxy;
5027 if (lpColumn->mask & LVCF_IMAGE)
5028 lpColumn->iImage = hdi.iImage;
5030 if (lpColumn->mask & LVCF_ORDER)
5031 lpColumn->iOrder = hdi.iOrder;
5039 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
5041 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5048 for (i = 0; i < iCount; i++)
5056 * Retrieves the column width.
5059 * [I] HWND : window handle
5060 * [I] int : column index
5063 * SUCCESS : column width
5066 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5068 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5069 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5070 INT nColumnWidth = 0;
5073 if (uView == LVS_LIST)
5075 nColumnWidth = infoPtr->nItemWidth;
5077 else if (uView == LVS_REPORT)
5079 /* get column width from header */
5080 ZeroMemory(&hdi, sizeof(hdi));
5081 hdi.mask = HDI_WIDTH;
5082 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5083 nColumnWidth = hdi.cxy;
5086 return nColumnWidth;
5091 * In list or report display mode, retrieves the number of items that can fit
5092 * vertically in the visible area. In icon or small icon display mode,
5093 * retrieves the total number of visible items.
5096 * [I] HWND : window handle
5099 * Number of fully visible items.
5101 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5103 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5104 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5107 if (uView == LVS_LIST)
5109 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5111 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5112 LISTVIEW_GetCountPerColumn(hwnd);
5115 else if (uView == LVS_REPORT)
5117 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5121 nItemCount = GETITEMCOUNT(infoPtr);
5127 /* LISTVIEW_GetEditControl */
5131 * Retrieves the extended listview style.
5134 * [I] HWND : window handle
5137 * SUCCESS : previous style
5140 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5142 LISTVIEW_INFO *infoPtr;
5144 /* make sure we can get the listview info */
5145 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5148 return (infoPtr->dwExStyle);
5153 * Retrieves the handle to the header control.
5156 * [I] HWND : window handle
5161 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5163 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5165 return infoPtr->hwndHeader;
5168 /* LISTVIEW_GetHotCursor */
5172 * Returns the time that the mouse cursor must hover over an item
5173 * before it is selected.
5176 * [I] HWND : window handle
5179 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5180 * hover time is set to the default hover time.
5182 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5184 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5186 return infoPtr->dwHoverTime;
5191 * Retrieves an image list handle.
5194 * [I] HWND : window handle
5195 * [I] INT : image list identifier
5198 * SUCCESS : image list handle
5201 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5203 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5204 HIMAGELIST himl = NULL;
5209 himl = infoPtr->himlNormal;
5212 himl = infoPtr->himlSmall;
5215 himl = infoPtr->himlState;
5219 return (LRESULT)himl;
5222 /* LISTVIEW_GetISearchString */
5226 * Retrieves item attributes.
5229 * [I] hwnd : window handle
5230 * [IO] lpLVItem : item info
5231 * [I] internal : if true then we will use tricks that avoid copies
5232 * but are not compatible with the regular interface
5233 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5234 * if FALSE, the lpLVItem is a LPLVITEMA.
5240 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5242 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5243 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5244 NMLVDISPINFOW dispInfo;
5245 LISTVIEW_SUBITEM *lpSubItem;
5246 LISTVIEW_ITEM *lpItem;
5249 INT* piImage = (INT*)&null;
5250 LPWSTR* ppszText= (LPWSTR*)&null;
5251 LPARAM* plParam = (LPARAM*)&null;
5253 if (internal && !isW)
5255 ERR("We can't have internal non-Unicode GetItem!\n");
5259 /* In the following:
5260 * lpLVItem describes the information requested by the user
5261 * lpItem/lpSubItem is what we have
5262 * dispInfo is a structure we use to request the missing
5263 * information from the application
5266 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5267 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5269 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5270 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5273 ZeroMemory(&dispInfo, sizeof(dispInfo));
5275 if (lStyle & LVS_OWNERDATA)
5277 if (lpLVItem->mask & ~LVIF_STATE)
5279 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5280 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5281 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5282 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5285 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5287 lpLVItem->state = 0;
5288 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5289 lpLVItem->state |= LVIS_FOCUSED;
5290 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5291 lpLVItem->state |= LVIS_SELECTED;
5297 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5298 if (hdpaSubItems == NULL) return FALSE;
5300 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5303 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5304 if (lpLVItem->iSubItem == 0)
5306 piImage=&lpItem->iImage;
5307 ppszText=&lpItem->pszText;
5308 plParam=&lpItem->lParam;
5309 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5311 dispInfo.item.mask |= LVIF_STATE;
5312 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5317 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5318 if (lpSubItem != NULL)
5320 piImage=&lpSubItem->iImage;
5321 ppszText=&lpSubItem->pszText;
5325 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5327 dispInfo.item.mask |= LVIF_IMAGE;
5330 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5332 dispInfo.item.mask |= LVIF_TEXT;
5333 dispInfo.item.pszText = lpLVItem->pszText;
5334 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5335 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5336 *dispInfo.item.pszText = '\0';
5337 if (dispInfo.item.pszText && (*ppszText == NULL))
5338 *dispInfo.item.pszText = '\0';
5341 if (dispInfo.item.mask != 0)
5343 /* We don't have all the requested info, query the application */
5344 dispInfo.item.iItem = lpLVItem->iItem;
5345 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5346 dispInfo.item.lParam = lpItem->lParam;
5347 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5348 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5351 if (dispInfo.item.mask & LVIF_IMAGE)
5353 lpLVItem->iImage = dispInfo.item.iImage;
5354 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5355 *piImage = dispInfo.item.iImage;
5357 else if (lpLVItem->mask & LVIF_IMAGE)
5359 lpLVItem->iImage = *piImage;
5362 if (dispInfo.item.mask & LVIF_PARAM)
5364 lpLVItem->lParam = dispInfo.item.lParam;
5365 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5366 *plParam = dispInfo.item.lParam;
5368 else if (lpLVItem->mask & LVIF_PARAM)
5369 lpLVItem->lParam = lpItem->lParam;
5371 if (dispInfo.item.mask & LVIF_TEXT)
5373 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5374 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5376 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5377 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5378 if (lpLVItem->pszText != dispInfo.item.pszText)
5379 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5382 else if (lpLVItem->mask & LVIF_TEXT)
5384 if (internal) lpLVItem->pszText = *ppszText;
5385 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5388 if (lpLVItem->iSubItem == 0)
5390 if (dispInfo.item.mask & LVIF_STATE)
5392 lpLVItem->state = lpItem->state;
5393 lpLVItem->state &= ~dispInfo.item.stateMask;
5394 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5396 lpLVItem->state &= ~LVIS_SELECTED;
5397 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5398 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5399 lpLVItem->state |= LVIS_SELECTED;
5401 else if (lpLVItem->mask & LVIF_STATE)
5403 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5405 lpLVItem->state &= ~LVIS_SELECTED;
5406 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5407 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5408 lpLVItem->state |= LVIS_SELECTED;
5411 if (lpLVItem->mask & LVIF_PARAM)
5412 lpLVItem->lParam = lpItem->lParam;
5414 if (lpLVItem->mask & LVIF_INDENT)
5415 lpLVItem->iIndent = lpItem->iIndent;
5421 /* LISTVIEW_GetHotCursor */
5425 * Retrieves the index of the hot item.
5428 * [I] HWND : window handle
5431 * SUCCESS : hot item index
5432 * FAILURE : -1 (no hot item)
5434 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5436 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5438 return infoPtr->nHotItem;
5441 /* LISTVIEW_GetHoverTime */
5445 * Retrieves the number of items in the listview control.
5448 * [I] HWND : window handle
5453 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5455 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5457 return GETITEMCOUNT(infoPtr);
5462 * Retrieves the rectangle enclosing the item icon and text.
5465 * [I] HWND : window handle
5466 * [I] INT : item index
5467 * [O] LPRECT : coordinate information
5473 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5475 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5476 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5477 UINT uView = lStyle & LVS_TYPEMASK;
5478 BOOL bResult = FALSE;
5480 LISTVIEW_ITEM *lpItem;
5481 INT nCountPerColumn;
5484 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5486 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5489 if (uView == LVS_LIST)
5492 nItem = nItem - ListView_GetTopIndex(hwnd);
5493 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5496 nRow = nItem % nCountPerColumn;
5499 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5504 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5505 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5510 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5511 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5514 else if (uView == LVS_REPORT)
5517 lpRect->left = REPORT_MARGINX;
5518 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5519 infoPtr->nItemHeight) + infoPtr->rcList.top;
5521 if (!(lStyle & LVS_NOSCROLL))
5523 SCROLLINFO scrollInfo;
5524 /* Adjust position by scrollbar offset */
5525 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5526 scrollInfo.cbSize = sizeof(SCROLLINFO);
5527 scrollInfo.fMask = SIF_POS;
5528 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5529 lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5532 else /* either LVS_ICON or LVS_SMALLICON */
5534 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5536 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5539 lpRect->left = lpItem->ptPosition.x;
5540 lpRect->top = lpItem->ptPosition.y;
5545 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5546 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5547 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5548 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5554 * Retrieves the position (upper-left) of the listview control item.
5555 * Note that for LVS_ICON style, the upper-left is that of the icon
5556 * and not the bounding box.
5559 * [I] HWND : window handle
5560 * [I] INT : item index
5561 * [O] LPPOINT : coordinate information
5567 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5569 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5570 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5571 BOOL bResult = FALSE;
5574 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5576 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5577 (lpptPosition != NULL))
5579 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5580 lpptPosition->x = rcBounding.left;
5581 lpptPosition->y = rcBounding.top;
5582 if (uView == LVS_ICON)
5584 lpptPosition->y += ICON_TOP_PADDING;
5585 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5587 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5588 lpptPosition->x, lpptPosition->y);
5594 * Update the bounding rectangle around the text under a large icon.
5595 * This depends on whether it has the focus or not.
5596 * On entry the rectangle's top, left and right should be set.
5597 * On return the bottom will also be set and the width may have been
5600 * This appears to be weird, even in the Microsoft implementation.
5603 static void ListView_UpdateLargeItemLabelRect (
5604 HWND hwnd, /* The window of the listview */
5605 const LISTVIEW_INFO *infoPtr, /* The listview itself */
5606 int nItem, /* The item for which we are calculating this */
5607 RECT *rect) /* The rectangle to be updated */
5609 HDC hdc = GetDC (hwnd);
5610 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5612 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5614 /* We (aim to) display the full text. In Windows 95 it appears to
5615 * calculate the size assuming the specified font and then it draws
5616 * the text in that region with the specified font except scaled to
5617 * 10 point (or the height of the system font or ...). Thus if the
5618 * window has 24 point Helvetica the highlit rectangle will be
5619 * taller than the text and if it is 7 point Helvetica then the text
5621 * For now we will simply say that it is the correct size to display
5622 * the text in the specified font.
5625 lvItem.mask = LVIF_TEXT;
5626 lvItem.iItem = nItem;
5627 lvItem.iSubItem = 0;
5628 /* We will specify INTERNAL and so will receive back a const
5629 * pointer to the text, rather than specifying a buffer to which
5632 LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5633 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5634 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5635 DT_WORDBREAK | DT_NOPREFIX);
5636 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5640 /* As far as I can see the text region seems to be trying to be
5641 * "tall enough for two lines of text". Once again (comctl32.dll ver
5642 * 5.81?) it measures this on the basis of the selected font and then
5643 * draws it with the same font except in 10 point size. This can lead
5644 * to more or less than the two rows appearing.
5645 * Question; are we supposed to be including DT_EXTERNALLEADING?
5646 * Question; should the width be shrunk to the space required to
5647 * display the two lines?
5649 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5652 SelectObject (hdc, hOldFont);
5653 ReleaseDC (hwnd, hdc);
5658 * Retrieves the bounding rectangle for a listview control item.
5661 * [I] HWND : window handle
5662 * [I] INT : item index
5663 * [IO] LPRECT : bounding rectangle coordinates
5664 * lprc->left specifies the portion of the item for which the bounding
5665 * rectangle will be retrieved.
5667 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5668 * including the icon and label.
5669 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5670 * LVIR_LABEL Returns the bounding rectangle of the item text.
5671 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5672 * rectangles, but excludes columns in report view.
5679 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5680 * upon whether the window has the focus currently and on whether the item
5681 * is the one with the focus. Ensure that the control's record of which
5682 * item has the focus agrees with the items' records.
5684 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5686 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5687 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5688 BOOL bResult = FALSE;
5697 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5699 if (uView & LVS_REPORT)
5701 ZeroMemory(&lvItem, sizeof(lvItem));
5702 lvItem.mask = LVIF_INDENT;
5703 lvItem.iItem = nItem;
5704 lvItem.iSubItem = 0;
5705 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5708 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5709 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5716 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5721 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5722 if (uView == LVS_ICON)
5724 if (infoPtr->himlNormal != NULL)
5726 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5729 lprc->left = ptItem.x + ptOrigin.x;
5730 lprc->top = ptItem.y + ptOrigin.y;
5731 lprc->right = lprc->left + infoPtr->iconSize.cx;
5732 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5733 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5737 else if (uView == LVS_SMALLICON)
5739 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5742 lprc->left = ptItem.x + ptOrigin.x;
5743 lprc->top = ptItem.y + ptOrigin.y;
5744 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5746 if (infoPtr->himlState != NULL)
5747 lprc->left += infoPtr->iconSize.cx;
5749 if (infoPtr->himlSmall != NULL)
5750 lprc->right = lprc->left + infoPtr->iconSize.cx;
5752 lprc->right = lprc->left;
5758 lprc->left = ptItem.x;
5759 if (uView & LVS_REPORT)
5760 lprc->left += nIndent;
5761 lprc->top = ptItem.y;
5762 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5764 if (infoPtr->himlState != NULL)
5765 lprc->left += infoPtr->iconSize.cx;
5767 if (infoPtr->himlSmall != NULL)
5768 lprc->right = lprc->left + infoPtr->iconSize.cx;
5770 lprc->right = lprc->left;
5775 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5776 if (uView == LVS_ICON)
5778 if (infoPtr->himlNormal != NULL)
5780 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5783 lprc->left = ptItem.x + ptOrigin.x;
5784 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5785 ICON_BOTTOM_PADDING);
5786 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5787 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5789 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5790 lprc->right = lprc->left + nLabelWidth;
5795 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5796 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5801 else if (uView == LVS_SMALLICON)
5803 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5806 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5807 lprc->top = ptItem.y + ptOrigin.y;
5808 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5810 if (infoPtr->himlState != NULL)
5811 lprc->left += infoPtr->iconSize.cx;
5813 if (infoPtr->himlSmall != NULL)
5814 lprc->left += infoPtr->iconSize.cx;
5816 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5817 nLabelWidth += TRAILING_PADDING;
5818 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5819 lprc->right = lprc->left + nLabelWidth;
5821 lprc->right = nLeftPos + infoPtr->nItemWidth;
5827 if (uView == LVS_REPORT)
5828 nLeftPos = lprc->left = ptItem.x + nIndent;
5830 nLeftPos = lprc->left = ptItem.x;
5831 lprc->top = ptItem.y;
5832 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5834 if (infoPtr->himlState != NULL)
5835 lprc->left += infoPtr->iconSize.cx;
5837 if (infoPtr->himlSmall != NULL)
5838 lprc->left += infoPtr->iconSize.cx;
5840 if (uView != LVS_REPORT)
5842 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5843 nLabelWidth += TRAILING_PADDING;
5844 if (infoPtr->himlSmall)
5845 nLabelWidth += IMAGE_PADDING;
5848 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5849 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5850 lprc->right = lprc->left + nLabelWidth;
5852 lprc->right = nLeftPos + infoPtr->nItemWidth;
5857 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5858 ptItem.x = rcInternal.left;
5859 ptItem.y = rcInternal.top;
5860 if (uView == LVS_ICON)
5862 if (infoPtr->himlNormal != NULL)
5864 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5867 INT text_left, text_right, icon_left, text_pos_x;
5868 /* for style LVS_ICON bounds
5869 * left = min(icon.left, text.left)
5870 * right = max(icon.right, text.right)
5871 * top = boundbox.top + NOTHITABLE
5872 * bottom = text.bottom + 1
5875 icon_left = text_left = ptItem.x;
5877 /* Correct ptItem to icon upper-left */
5878 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5879 ptItem.y += ICON_TOP_PADDING;
5881 /* Compute the label left and right */
5882 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5883 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5886 text_left += text_pos_x / 2;
5887 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5892 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5895 /* Compute rectangle w/o the text height */
5896 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5897 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5898 text_right) + ptOrigin.x;
5899 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5900 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5901 + infoPtr->iconSize.cy + 1
5902 + ICON_BOTTOM_PADDING;
5904 CopyRect (&label_rect, lprc);
5905 label_rect.top = lprc->bottom;
5906 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5907 UnionRect (lprc, lprc, &label_rect);
5911 else if (uView == LVS_SMALLICON)
5913 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5916 lprc->left = ptItem.x + ptOrigin.x;
5917 lprc->right = lprc->left;
5918 lprc->top = ptItem.y + ptOrigin.y;
5919 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5920 if (infoPtr->himlState != NULL)
5921 lprc->right += infoPtr->iconSize.cx;
5922 if (infoPtr->himlSmall != NULL)
5923 lprc->right += infoPtr->iconSize.cx;
5925 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5926 nLabelWidth += TRAILING_PADDING;
5927 if (infoPtr->himlSmall)
5928 nLabelWidth += IMAGE_PADDING;
5929 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5930 lprc->right += nLabelWidth;
5932 lprc->right = lprc->left + infoPtr->nItemWidth;
5938 lprc->left = ptItem.x;
5939 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5940 lprc->left += nIndent;
5941 lprc->right = lprc->left;
5942 lprc->top = ptItem.y;
5943 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5945 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5948 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5949 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5951 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5955 if (infoPtr->himlState != NULL)
5956 lprc->right += infoPtr->iconSize.cx;
5958 if (infoPtr->himlSmall != NULL)
5959 lprc->right += infoPtr->iconSize.cx;
5961 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5962 nLabelWidth += TRAILING_PADDING;
5963 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5964 lprc->right += nLabelWidth;
5966 lprc->right = lprc->left + infoPtr->nItemWidth;
5971 case LVIR_SELECTBOUNDS:
5972 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5973 if (uView == LVS_ICON)
5975 if (infoPtr->himlNormal != NULL)
5977 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5980 lprc->left = ptItem.x + ptOrigin.x;
5981 lprc->top = ptItem.y + ptOrigin.y;
5982 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5983 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5987 else if (uView == LVS_SMALLICON)
5989 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5992 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5993 lprc->top = ptItem.y + ptOrigin.y;
5994 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5996 if (infoPtr->himlState != NULL)
5997 lprc->left += infoPtr->iconSize.cx;
5999 lprc->right = lprc->left;
6001 if (infoPtr->himlSmall != NULL)
6002 lprc->right += infoPtr->iconSize.cx;
6004 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6005 nLabelWidth += TRAILING_PADDING;
6006 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6007 lprc->right += nLabelWidth;
6009 lprc->right = nLeftPos + infoPtr->nItemWidth;
6015 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
6016 nLeftPos = lprc->left = ptItem.x + nIndent;
6018 nLeftPos = lprc->left = ptItem.x;
6019 lprc->top = ptItem.y;
6020 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6022 if (infoPtr->himlState != NULL)
6023 lprc->left += infoPtr->iconSize.cx;
6025 lprc->right = lprc->left;
6027 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
6030 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6031 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6033 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6037 if (infoPtr->himlSmall != NULL)
6038 lprc->right += infoPtr->iconSize.cx;
6040 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6041 nLabelWidth += TRAILING_PADDING;
6042 if (infoPtr->himlSmall)
6043 nLabelWidth += IMAGE_PADDING;
6044 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6045 lprc->right += nLabelWidth;
6047 lprc->right = nLeftPos + infoPtr->nItemWidth;
6054 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6055 lprc->left, lprc->top, lprc->right, lprc->bottom);
6061 static LRESULT LISTVIEW_GetSubItemRect(HWND hwnd, INT nItem, INT nSubItem, INT
6064 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6067 TRACE("(hwnd=%x, nItem=%d, nSubItem=%d lprc=%p)\n", hwnd, nItem, nSubItem,
6070 if (!(uView & LVS_REPORT))
6073 if (flags & LVIR_ICON)
6075 FIXME("Unimplemented LVIR_ICON\n");
6080 LISTVIEW_GetItemRect(hwnd,nItem,lprc);
6082 while (count<(nSubItem-1))
6084 lprc->left += LISTVIEW_GetColumnWidth(hwnd,count);
6088 lprc->right = LISTVIEW_GetColumnWidth(hwnd,(nSubItem-1)) +
6098 * Retrieves the width of a label.
6101 * [I] HWND : window handle
6104 * SUCCESS : string width (in pixels)
6107 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6109 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6110 INT nLabelWidth = 0;
6113 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6115 ZeroMemory(&lvItem, sizeof(lvItem));
6116 lvItem.mask = LVIF_TEXT;
6117 lvItem.iItem = nItem;
6118 lvItem.cchTextMax = DISP_TEXT_SIZE;
6119 lvItem.pszText = szDispText;
6120 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6121 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
6128 * Retrieves the spacing between listview control items.
6131 * [I] HWND : window handle
6132 * [I] BOOL : flag for small or large icon
6135 * Horizontal + vertical spacing
6137 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6139 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6142 if (bSmall == FALSE)
6144 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6148 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6149 if ((style & LVS_TYPEMASK) == LVS_ICON)
6150 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6152 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6159 * Retrieves the state of a listview control item.
6162 * [I] HWND : window handle
6163 * [I] INT : item index
6164 * [I] UINT : state mask
6167 * State specified by the mask.
6169 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6171 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6175 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6177 ZeroMemory(&lvItem, sizeof(lvItem));
6178 lvItem.iItem = nItem;
6179 lvItem.stateMask = uMask;
6180 lvItem.mask = LVIF_STATE;
6181 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6182 uState = lvItem.state;
6190 * Retrieves the text of a listview control item or subitem.
6193 * [I] hwnd : window handle
6194 * [I] nItem : item index
6195 * [IO] lpLVItem : item information
6196 * [I] isW : TRUE if lpLVItem is Unicode
6199 * SUCCESS : string length
6202 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6204 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6207 if (lpLVItem != NULL)
6209 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6211 lpLVItem->mask = LVIF_TEXT;
6212 lpLVItem->iItem = nItem;
6213 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6214 nLength = textlenT(lpLVItem->pszText, isW);
6223 * Searches for an item based on properties + relationships.
6226 * [I] HWND : window handle
6227 * [I] INT : item index
6228 * [I] INT : relationship flag
6231 * SUCCESS : item index
6234 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6236 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6237 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6239 LVFINDINFOW lvFindInfo;
6240 INT nCountPerColumn;
6243 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6245 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6247 if (uFlags & LVNI_CUT)
6250 if (uFlags & LVNI_DROPHILITED)
6251 uMask |= LVIS_DROPHILITED;
6253 if (uFlags & LVNI_FOCUSED)
6254 uMask |= LVIS_FOCUSED;
6256 if (uFlags & LVNI_SELECTED)
6257 uMask |= LVIS_SELECTED;
6259 if (uFlags & LVNI_ABOVE)
6261 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6266 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6272 lvFindInfo.flags = LVFI_NEARESTXY;
6273 lvFindInfo.vkDirection = VK_UP;
6274 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6275 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6277 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6282 else if (uFlags & LVNI_BELOW)
6284 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6286 while (nItem < GETITEMCOUNT(infoPtr))
6289 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6295 lvFindInfo.flags = LVFI_NEARESTXY;
6296 lvFindInfo.vkDirection = VK_DOWN;
6297 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6298 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6300 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6305 else if (uFlags & LVNI_TOLEFT)
6307 if (uView == LVS_LIST)
6309 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6310 while (nItem - nCountPerColumn >= 0)
6312 nItem -= nCountPerColumn;
6313 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6317 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6319 lvFindInfo.flags = LVFI_NEARESTXY;
6320 lvFindInfo.vkDirection = VK_LEFT;
6321 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6322 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6324 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6329 else if (uFlags & LVNI_TORIGHT)
6331 if (uView == LVS_LIST)
6333 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6334 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6336 nItem += nCountPerColumn;
6337 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6341 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6343 lvFindInfo.flags = LVFI_NEARESTXY;
6344 lvFindInfo.vkDirection = VK_RIGHT;
6345 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6346 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6348 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6357 /* search by index */
6358 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6360 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6369 /* LISTVIEW_GetNumberOfWorkAreas */
6373 * Retrieves the origin coordinates when in icon or small icon display mode.
6376 * [I] HWND : window handle
6377 * [O] LPPOINT : coordinate information
6383 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6385 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6386 UINT uView = lStyle & LVS_TYPEMASK;
6387 BOOL bResult = FALSE;
6389 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6391 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6393 SCROLLINFO scrollInfo;
6394 ZeroMemory(lpptOrigin, sizeof(POINT));
6395 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6396 scrollInfo.cbSize = sizeof(SCROLLINFO);
6398 if (lStyle & WS_HSCROLL)
6400 scrollInfo.fMask = SIF_POS;
6401 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6402 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6405 if (lStyle & WS_VSCROLL)
6407 scrollInfo.fMask = SIF_POS;
6408 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6409 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6420 * Retrieves the number of items that are marked as selected.
6423 * [I] HWND : window handle
6426 * Number of items selected.
6428 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6431 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6432 INT nSelectedCount = 0;
6435 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6437 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6441 return nSelectedCount;
6446 * Retrieves item index that marks the start of a multiple selection.
6449 * [I] HWND : window handle
6452 * Index number or -1 if there is no selection mark.
6454 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6456 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6458 return infoPtr->nSelectionMark;
6464 * Retrieves the width of a string.
6467 * [I] hwnd : window handle
6468 * [I] lpszText : text string to process
6469 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6472 * SUCCESS : string width (in pixels)
6475 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6477 if (is_textT(lpszText, isW))
6479 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6480 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6481 HDC hdc = GetDC(hwnd);
6482 HFONT hOldFont = SelectObject(hdc, hFont);
6484 ZeroMemory(&stringSize, sizeof(SIZE));
6486 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6488 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6489 SelectObject(hdc, hOldFont);
6490 ReleaseDC(hwnd, hdc);
6491 return stringSize.cx;
6498 * Retrieves the text backgound color.
6501 * [I] HWND : window handle
6504 * COLORREF associated with the the background.
6506 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6508 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6510 return infoPtr->clrTextBk;
6515 * Retrieves the text color.
6518 * [I] HWND : window handle
6521 * COLORREF associated with the text.
6523 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6525 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6527 return infoPtr->clrText;
6532 * Determines item if a hit or closest if not
6535 * [I] HWND : window handle
6536 * [IO] LPLV_INTHIT : hit test information
6537 * [I] subitem : fill out iSubItem.
6540 * SUCCESS : item index of hit
6543 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6545 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6546 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6547 UINT uView = lStyle & LVS_TYPEMASK;
6548 INT i,topindex,bottomindex;
6550 DWORD xterm, yterm, dist;
6552 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6554 topindex = ListView_GetTopIndex(hwnd);
6555 if (uView == LVS_REPORT)
6557 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6558 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6562 bottomindex = GETITEMCOUNT(infoPtr);
6565 lpInt->distance = 0x7fffffff;
6566 lpInt->iDistItem = -1;
6568 for (i = topindex; i < bottomindex; i++)
6570 rcItem.left = LVIR_BOUNDS;
6571 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6573 if (PtInRect(&rcItem, lpInt->ht.pt))
6575 rcItem.left = LVIR_ICON;
6576 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6578 if (PtInRect(&rcItem, lpInt->ht.pt))
6580 lpInt->ht.flags = LVHT_ONITEMICON;
6581 lpInt->ht.iItem = i;
6582 if (subitem) lpInt->ht.iSubItem = 0;
6587 rcItem.left = LVIR_LABEL;
6588 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6590 if (PtInRect(&rcItem, lpInt->ht.pt))
6592 lpInt->ht.flags = LVHT_ONITEMLABEL;
6593 lpInt->ht.iItem = i;
6594 if (subitem) lpInt->ht.iSubItem = 0;
6599 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6600 lpInt->ht.iItem = i;
6601 if (subitem) lpInt->ht.iSubItem = 0;
6607 * Now compute distance from point to center of boundary
6608 * box. Since we are only interested in the relative
6609 * distance, we can skip the nasty square root operation
6611 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6612 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6613 dist = xterm * xterm + yterm * yterm;
6614 if (dist < lpInt->distance)
6616 lpInt->distance = dist;
6617 lpInt->iDistItem = i;
6623 lpInt->ht.flags = LVHT_NOWHERE;
6624 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6631 * Determines which section of the item was selected (if any).
6634 * [I] HWND : window handle
6635 * [IO] LPLVHITTESTINFO : hit test information
6636 * [I] subitem : fill out iSubItem.
6639 * SUCCESS : item index
6642 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6645 LV_INTHIT lv_inthit;
6647 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6648 lpHitTestInfo->pt.y);
6650 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6651 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6652 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6658 * Determines which listview item is located at the specified position.
6661 * [I] HWND : window handle
6662 * [IO} LPLVHITTESTINFO : hit test information
6665 * SUCCESS : item index
6668 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6670 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6673 lpHitTestInfo->flags = 0;
6675 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6676 lpHitTestInfo->flags = LVHT_TOLEFT;
6677 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6678 lpHitTestInfo->flags = LVHT_TORIGHT;
6679 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6680 lpHitTestInfo->flags |= LVHT_ABOVE;
6681 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6682 lpHitTestInfo->flags |= LVHT_BELOW;
6684 if (lpHitTestInfo->flags == 0)
6686 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6687 * an app might pass only a structure with space up to iItem!
6688 * (MS Office 97 does that for instance in the file open dialog)
6690 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6698 * Inserts a new column.
6701 * [I] HWND : window handle
6702 * [I] INT : column index
6703 * [I] LPLVCOLUMNW : column information
6706 * SUCCESS : new column index
6709 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6710 LPLVCOLUMNW lpColumn, BOOL isW)
6712 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6713 INT nNewColumn = -1;
6716 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6718 if (lpColumn != NULL)
6720 /* initialize memory */
6721 ZeroMemory(&hdi, sizeof(hdi));
6723 if (lpColumn->mask & LVCF_FMT)
6725 /* format member is valid */
6726 hdi.mask |= HDI_FORMAT;
6728 /* set text alignment (leftmost column must be left-aligned) */
6731 hdi.fmt |= HDF_LEFT;
6735 if (lpColumn->fmt & LVCFMT_LEFT)
6737 hdi.fmt |= HDF_LEFT;
6739 else if (lpColumn->fmt & LVCFMT_RIGHT)
6741 hdi.fmt |= HDF_RIGHT;
6743 else if (lpColumn->fmt & LVCFMT_CENTER)
6745 hdi.fmt |= HDF_CENTER;
6749 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6751 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6755 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6760 if (lpColumn->fmt & LVCFMT_IMAGE)
6762 hdi.fmt |= HDF_IMAGE;
6763 hdi.iImage = I_IMAGECALLBACK;
6767 if (lpColumn->mask & LVCF_WIDTH)
6769 hdi.mask |= HDI_WIDTH;
6770 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6772 /* make it fill the remainder of the controls width */
6777 ZeroMemory(&hdit, sizeof(hdit));
6779 /* get the width of every item except the current one */
6780 hdit.mask = HDI_WIDTH;
6783 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6784 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6788 /* retrieve the layout of the header */
6789 GetClientRect(hwnd, &rcHeader);
6790 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6791 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6793 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6796 hdi.cxy = lpColumn->cx;
6799 if (lpColumn->mask & LVCF_TEXT)
6801 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6802 hdi.pszText = lpColumn->pszText;
6803 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6804 hdi.fmt |= HDF_STRING;
6807 if (lpColumn->mask & LVCF_IMAGE)
6809 hdi.mask |= HDI_IMAGE;
6810 hdi.iImage = lpColumn->iImage;
6813 if (lpColumn->mask & LVCF_ORDER)
6815 hdi.mask |= HDI_ORDER;
6816 hdi.iOrder = lpColumn->iOrder;
6819 /* insert item in header control */
6820 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6821 (WPARAM)nColumn, (LPARAM)&hdi);
6823 /* Need to reset the item width when inserting a new column */
6824 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6826 LISTVIEW_UpdateScroll(hwnd);
6827 InvalidateRect(hwnd, NULL, FALSE);
6833 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6834 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6835 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6836 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6837 their own sort proc. when sending LVM_SORTITEMS.
6840 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6842 LVS_SORTXXX must be specified,
6843 LVS_OWNERDRAW is not set,
6844 <item>.pszText is not LPSTR_TEXTCALLBACK.
6846 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6847 are sorted based on item text..."
6849 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6851 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6852 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6853 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6854 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6855 /* if we're sorting descending, negate the return value */
6856 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6861 * Inserts a new item in the listview control.
6864 * [I] HWND : window handle
6865 * [I] LPLVITEMW : item information
6866 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6869 * SUCCESS : new item index
6872 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6874 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6875 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6876 UINT uView = lStyle & LVS_TYPEMASK;
6880 LISTVIEW_ITEM *lpItem = NULL;
6882 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6883 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6885 if (lStyle & LVS_OWNERDATA)
6887 nItem = infoPtr->hdpaItems->nItemCount;
6888 infoPtr->hdpaItems->nItemCount ++;
6892 if (lpLVItem != NULL)
6894 /* make sure it's not a subitem; cannot insert a subitem */
6895 if (lpLVItem->iSubItem == 0)
6897 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6899 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6900 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6902 /* insert item in listview control data structure */
6903 if ( (hdpaSubItems = DPA_Create(8)) )
6905 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6907 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6908 && !(lStyle & LVS_OWNERDRAWFIXED)
6909 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6911 /* Insert the item in the proper sort order based on the pszText
6912 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6913 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6914 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6915 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6916 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6920 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6927 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6929 /* manage item focus */
6930 if (lpLVItem->mask & LVIF_STATE)
6932 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6933 if (lpLVItem->stateMask & LVIS_SELECTED)
6934 LISTVIEW_SetSelection(hwnd, nItem);
6935 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6936 LISTVIEW_SetItemFocus(hwnd, nItem);
6939 /* send LVN_INSERTITEM notification */
6940 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6942 nmlv.lParam = lpItem->lParam;
6943 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6945 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6947 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6948 if (nItemWidth > infoPtr->nItemWidth)
6949 infoPtr->nItemWidth = nItemWidth;
6952 /* align items (set position of each item) */
6953 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6955 if (lStyle & LVS_ALIGNLEFT)
6956 LISTVIEW_AlignLeft(hwnd);
6958 LISTVIEW_AlignTop(hwnd);
6961 LISTVIEW_UpdateScroll(hwnd);
6962 /* refresh client area */
6963 InvalidateRect(hwnd, NULL, FALSE);
6972 /* free memory if unsuccessful */
6973 if ((nItem == -1) && (lpItem != NULL))
6974 COMCTL32_Free(lpItem);
6981 * Redraws a range of items.
6984 * [I] HWND : window handle
6985 * [I] INT : first item
6986 * [I] INT : last item
6992 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6994 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6995 BOOL bResult = FALSE;
6999 if (nFirst <= nLast)
7001 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
7003 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
7005 for (i = nFirst; i <= nLast; i++)
7007 rcItem.left = LVIR_BOUNDS;
7008 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
7009 InvalidateRect(hwnd, &rcItem, TRUE);
7018 /* LISTVIEW_Scroll */
7022 * Sets the background color.
7025 * [I] HWND : window handle
7026 * [I] COLORREF : background color
7032 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
7034 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7036 if(infoPtr->clrBk!=clrBk){
7037 infoPtr->clrBk = clrBk;
7038 InvalidateRect(hwnd, NULL, TRUE);
7044 /* LISTVIEW_SetBkImage */
7048 * Sets the callback mask. This mask will be used when the parent
7049 * window stores state information (some or all).
7052 * [I] HWND : window handle
7053 * [I] UINT : state mask
7059 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
7061 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7063 infoPtr->uCallbackMask = uMask;
7070 * Sets the attributes of a header item.
7073 * [I] HWND : window handle
7074 * [I] INT : column index
7075 * [I] LPLVCOLUMNW : column attributes
7076 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
7077 * otherwise it is in fact a LPLVCOLUMNA
7083 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
7084 LPLVCOLUMNW lpColumn, BOOL isW)
7086 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7087 BOOL bResult = FALSE;
7088 HDITEMW hdi, hdiget;
7090 if ((lpColumn != NULL) && (nColumn >= 0) &&
7091 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7093 /* initialize memory */
7094 ZeroMemory(&hdi, sizeof(hdi));
7096 if (lpColumn->mask & LVCF_FMT)
7098 /* format member is valid */
7099 hdi.mask |= HDI_FORMAT;
7101 /* get current format first */
7102 hdiget.mask = HDI_FORMAT;
7103 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7104 /* preserve HDF_STRING if present */
7105 hdi.fmt = hdiget.fmt & HDF_STRING;
7107 /* set text alignment (leftmost column must be left-aligned) */
7110 hdi.fmt |= HDF_LEFT;
7114 if (lpColumn->fmt & LVCFMT_LEFT)
7115 hdi.fmt |= HDF_LEFT;
7116 else if (lpColumn->fmt & LVCFMT_RIGHT)
7117 hdi.fmt |= HDF_RIGHT;
7118 else if (lpColumn->fmt & LVCFMT_CENTER)
7119 hdi.fmt |= HDF_CENTER;
7122 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7123 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7125 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7126 hdi.fmt |= HDF_IMAGE;
7128 if (lpColumn->fmt & LVCFMT_IMAGE)
7130 hdi.fmt |= HDF_IMAGE;
7131 hdi.iImage = I_IMAGECALLBACK;
7135 if (lpColumn->mask & LVCF_WIDTH)
7137 hdi.mask |= HDI_WIDTH;
7138 hdi.cxy = lpColumn->cx;
7141 if (lpColumn->mask & LVCF_TEXT)
7143 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7144 hdi.pszText = lpColumn->pszText;
7145 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7146 hdi.fmt |= HDF_STRING;
7149 if (lpColumn->mask & LVCF_IMAGE)
7151 hdi.mask |= HDI_IMAGE;
7152 hdi.iImage = lpColumn->iImage;
7155 if (lpColumn->mask & LVCF_ORDER)
7157 hdi.mask |= HDI_ORDER;
7158 hdi.iOrder = lpColumn->iOrder;
7161 /* set header item attributes */
7163 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7165 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
7173 * Sets the column order array
7176 * [I] HWND : window handle
7177 * [I] INT : number of elements in column order array
7178 * [I] INT : pointer to column order array
7184 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7186 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7188 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7199 * Sets the width of a column
7202 * [I] HWND : window handle
7203 * [I] INT : column index
7204 * [I] INT : column width
7210 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7212 LISTVIEW_INFO *infoPtr;
7215 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7216 UINT uView = lStyle & LVS_TYPEMASK;
7221 WCHAR text_buffer[DISP_TEXT_SIZE];
7222 INT header_item_count;
7227 WCHAR szDispText[DISP_TEXT_SIZE];
7229 /* make sure we can get the listview info */
7230 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7233 if (!infoPtr->hwndHeader) /* make sure we have a header */
7236 /* set column width only if in report or list mode */
7237 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7240 TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7242 /* take care of invalid cx values */
7243 if((uView == LVS_REPORT) && (cx < -2))
7244 cx = LVSCW_AUTOSIZE;
7245 else if (uView == LVS_LIST && (cx < 1))
7248 /* resize all columns if in LVS_LIST mode */
7249 if(uView == LVS_LIST) {
7250 infoPtr->nItemWidth = cx;
7251 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7255 /* autosize based on listview items width */
7256 if(cx == LVSCW_AUTOSIZE)
7258 /* set the width of the column to the width of the widest item */
7259 if (iCol == 0 || uView == LVS_LIST)
7262 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7264 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7265 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7267 if (infoPtr->himlSmall)
7268 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7272 ZeroMemory(&lvItem, sizeof(lvItem));
7273 lvItem.iSubItem = iCol;
7274 lvItem.mask = LVIF_TEXT;
7275 lvItem.cchTextMax = DISP_TEXT_SIZE;
7276 lvItem.pszText = szDispText;
7277 *lvItem.pszText = '\0';
7279 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7281 lvItem.iItem = item_index;
7282 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7283 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7284 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7287 cx += TRAILING_PADDING;
7288 } /* autosize based on listview header width */
7289 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7291 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7293 /* if iCol is the last column make it fill the remainder of the controls width */
7294 if(iCol == (header_item_count - 1)) {
7295 /* get the width of every item except the current one */
7296 hdi.mask = HDI_WIDTH;
7299 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7300 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7304 /* retrieve the layout of the header */
7305 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7307 cx = (rcHeader.right - rcHeader.left) - cx;
7311 /* Despite what the MS docs say, if this is not the last
7312 column, then MS resizes the column to the width of the
7313 largest text string in the column, including headers
7314 and items. This is different from LVSCW_AUTOSIZE in that
7315 LVSCW_AUTOSIZE ignores the header string length.
7318 /* retrieve header font */
7319 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7321 /* retrieve header text */
7322 hdi.mask = HDI_TEXT;
7323 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7324 hdi.pszText = text_buffer;
7326 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7328 /* determine the width of the text in the header */
7330 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7332 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7334 SelectObject(hdc, old_font); /* restore the old font */
7335 ReleaseDC(hwnd, hdc);
7337 ZeroMemory(&lvItem, sizeof(lvItem));
7338 lvItem.iSubItem = iCol;
7339 lvItem.mask = LVIF_TEXT;
7340 lvItem.cchTextMax = DISP_TEXT_SIZE;
7341 lvItem.pszText = szDispText;
7342 *lvItem.pszText = '\0';
7344 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7346 lvItem.iItem = item_index;
7347 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7348 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7349 nLabelWidth += TRAILING_PADDING;
7350 /* While it is possible for subitems to have icons, even MS messes
7351 up the positioning, so I suspect no applications actually use
7353 if (item_index == 0 && infoPtr->himlSmall)
7354 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7355 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7360 /* call header to update the column change */
7361 hdi.mask = HDI_WIDTH;
7364 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7366 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7373 * Sets the extended listview style.
7376 * [I] HWND : window handle
7381 * SUCCESS : previous style
7384 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7386 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7387 DWORD dwOldStyle = infoPtr->dwExStyle;
7391 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7393 infoPtr->dwExStyle = dwStyle;
7398 /* LISTVIEW_SetHotCursor */
7402 * Sets the hot item index.
7405 * [I] HWND : window handle
7409 * SUCCESS : previous hot item index
7410 * FAILURE : -1 (no hot item)
7412 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7414 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7415 INT iOldIndex = infoPtr->nHotItem;
7418 infoPtr->nHotItem = iIndex;
7425 * Sets the amount of time the cursor must hover over an item before it is selected.
7428 * [I] HWND : window handle
7429 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7432 * Returns the previous hover time
7434 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7436 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7437 DWORD oldHoverTime = infoPtr->dwHoverTime;
7439 infoPtr->dwHoverTime = dwHoverTime;
7441 return oldHoverTime;
7446 * Sets spacing for icons of LVS_ICON style.
7449 * [I] HWND : window handle
7450 * [I] DWORD : MAKELONG(cx, cy)
7453 * MAKELONG(oldcx, oldcy)
7455 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7457 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7458 INT cy = HIWORD(spacing);
7459 INT cx = LOWORD(spacing);
7461 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7462 UINT uView = lStyle & LVS_TYPEMASK;
7464 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7465 if (cx == -1) /* set to default */
7466 cx = GetSystemMetrics(SM_CXICONSPACING);
7467 if (cy == -1) /* set to default */
7468 cy = GetSystemMetrics(SM_CYICONSPACING);
7471 infoPtr->iconSpacing.cx = cx;
7473 { /* if 0 then compute width */
7474 if (uView == LVS_ICON)
7475 FIXME("width computation not yet done\n");
7477 * Should scan each item and determine max width of
7478 * icon or label, then make that the width
7480 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7481 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7484 infoPtr->iconSpacing.cy = cy;
7486 { /* if 0 then compute height */
7487 if (uView == LVS_ICON)
7488 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7489 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7490 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7491 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7492 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7495 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7496 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7498 /* these depend on the iconSpacing */
7499 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7500 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7510 * [I] HWND : window handle
7511 * [I] INT : image list type
7512 * [I] HIMAGELIST : image list handle
7515 * SUCCESS : old image list
7518 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7520 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7521 HIMAGELIST himlOld = 0;
7527 himlOld = infoPtr->himlNormal;
7528 infoPtr->himlNormal = himl;
7532 himlOld = infoPtr->himlSmall;
7533 infoPtr->himlSmall = himl;
7537 himlOld = infoPtr->himlState;
7538 infoPtr->himlState = himl;
7539 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7543 oldHeight = infoPtr->nItemHeight;
7544 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7545 if (infoPtr->nItemHeight != oldHeight)
7546 LISTVIEW_UpdateScroll(hwnd);
7553 * Preallocates memory (does *not* set the actual count of items !)
7556 * [I] HWND : window handle
7557 * [I] INT : item count (projected number of items to allocate)
7558 * [I] DWORD : update flags
7564 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7566 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7568 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7570 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7572 int precount,topvisible;
7574 TRACE("LVS_OWNERDATA is set!\n");
7575 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7576 FIXME("flags %s %s not implemented\n",
7577 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7579 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7582 * Internally remove all the selections.
7586 LISTVIEW_SELECTION *selection;
7587 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7589 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7592 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7594 precount = infoPtr->hdpaItems->nItemCount;
7595 topvisible = ListView_GetTopIndex(hwnd) +
7596 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7598 infoPtr->hdpaItems->nItemCount = nItems;
7600 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(hwnd),
7601 DEFAULT_COLUMN_WIDTH);
7603 LISTVIEW_UpdateSize(hwnd);
7604 LISTVIEW_UpdateScroll(hwnd);
7606 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7607 InvalidateRect(hwnd, NULL, TRUE);
7611 /* According to MSDN for non-LVS_OWNERDATA this is just
7612 * a performance issue. The control allocates its internal
7613 * data structures for the number of items specified. It
7614 * cuts down on the number of memory allocations. Therefore
7615 * we will just issue a WARN here
7617 WARN("for non-ownerdata performance option not implemented.\n");
7625 * Sets the position of an item.
7628 * [I] HWND : window handle
7629 * [I] INT : item index
7630 * [I] LONG : x coordinate
7631 * [I] LONG : y coordinate
7637 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7638 LONG nPosX, LONG nPosY)
7640 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7641 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7642 UINT uView = lStyle & LVS_TYPEMASK;
7643 LISTVIEW_ITEM *lpItem;
7645 BOOL bResult = FALSE;
7647 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7649 if (lStyle & LVS_OWNERDATA)
7652 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7654 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7656 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7658 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7662 orig = lpItem->ptPosition;
7663 if ((nPosX == -1) && (nPosY == -1))
7665 /* This point value seems to be an undocumented feature. The
7666 * best guess is that it means either at the origin, or at
7667 * the true beginning of the list. I will assume the origin.
7670 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7677 if (uView == LVS_ICON)
7679 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7680 nPosY += ICON_TOP_PADDING;
7682 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7686 lpItem->ptPosition.x = nPosX;
7687 lpItem->ptPosition.y = nPosY;
7688 if (uView == LVS_ICON)
7690 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7691 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7692 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7694 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7695 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7698 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7699 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7704 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7705 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7718 * Sets the state of one or many items.
7721 * [I] HWND : window handle
7722 * [I]INT : item index
7723 * [I] LPLVITEM : item or subitem info
7729 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7731 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7732 BOOL bResult = TRUE;
7735 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7736 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7738 ZeroMemory(&lvItem, sizeof(lvItem));
7739 lvItem.mask = LVIF_STATE;
7740 lvItem.state = lpLVItem->state;
7741 lvItem.stateMask = lpLVItem->stateMask ;
7742 lvItem.iItem = nItem;
7746 /* apply to all items */
7747 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7748 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7751 bResult = ListView_SetItemW(hwnd, &lvItem);
7758 * Sets the text of an item or subitem.
7761 * [I] hwnd : window handle
7762 * [I] nItem : item index
7763 * [I] lpLVItem : item or subitem info
7764 * [I] isW : TRUE if input is Unicode
7770 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7772 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7773 BOOL bResult = FALSE;
7776 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7777 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7779 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7781 ZeroMemory(&lvItem, sizeof(LVITEMW));
7782 lvItem.mask = LVIF_TEXT;
7783 lvItem.pszText = lpLVItem->pszText;
7784 lvItem.iItem = nItem;
7785 lvItem.iSubItem = lpLVItem->iSubItem;
7786 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7787 else bResult = ListView_SetItemA(hwnd, &lvItem);
7795 * Set item index that marks the start of a multiple selection.
7798 * [I] HWND : window handle
7802 * Index number or -1 if there is no selection mark.
7804 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7806 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7807 INT nOldIndex = infoPtr->nSelectionMark;
7809 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7811 infoPtr->nSelectionMark = nIndex;
7818 * Sets the text background color.
7821 * [I] HWND : window handle
7822 * [I] COLORREF : text background color
7828 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7830 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7832 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7834 infoPtr->clrTextBk = clrTextBk;
7835 InvalidateRect(hwnd, NULL, TRUE);
7842 * Sets the text foreground color.
7845 * [I] HWND : window handle
7846 * [I] COLORREF : text color
7852 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7854 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7856 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7858 infoPtr->clrText = clrText;
7859 InvalidateRect(hwnd, NULL, TRUE);
7864 /* LISTVIEW_SetToolTips */
7865 /* LISTVIEW_SetUnicodeFormat */
7866 /* LISTVIEW_SetWorkAreas */
7870 * Callback internally used by LISTVIEW_SortItems()
7873 * [I] LPVOID : first LISTVIEW_ITEM to compare
7874 * [I] LPVOID : second LISTVIEW_ITEM to compare
7875 * [I] LPARAM : HWND of control
7878 * if first comes before second : negative
7879 * if first comes after second : positive
7880 * if first and second are equivalent : zero
7882 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7884 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7885 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7886 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7888 /* Forward the call to the client defined callback */
7889 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7894 * Sorts the listview items.
7897 * [I] HWND : window handle
7898 * [I] WPARAM : application-defined value
7899 * [I] LPARAM : pointer to comparision callback
7905 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7907 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7908 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7909 HDPA hdpaSubItems=NULL;
7910 LISTVIEW_ITEM *pLVItem=NULL;
7911 LPVOID selectionMarkItem;
7914 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7916 if (lStyle & LVS_OWNERDATA) return FALSE;
7918 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7920 nCount = GETITEMCOUNT(infoPtr);
7921 /* if there are 0 or 1 items, there is no need to sort */
7925 infoPtr->pfnCompare = pfnCompare;
7926 infoPtr->lParamSort = lParamSort;
7927 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7929 /* Adjust selections and indices so that they are the way they should
7930 * be after the sort (otherwise, the list items move around, but
7931 * whatever is at the item's previous original position will be
7934 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7935 for (i=0; i < nCount; i++)
7937 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7938 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7940 if (pLVItem->state & LVIS_SELECTED)
7941 LISTVIEW_AddSelectionRange(hwnd, i, i);
7943 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7944 if (pLVItem->state & LVIS_FOCUSED)
7945 infoPtr->nFocusedItem=i;
7947 if (selectionMarkItem != NULL)
7948 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7949 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7951 /* align the items */
7952 LISTVIEW_AlignTop(hwnd);
7954 /* refresh the display */
7955 InvalidateRect(hwnd, NULL, TRUE);
7960 /* LISTVIEW_SubItemHitTest */
7964 * Updates an items or rearranges the listview control.
7967 * [I] HWND : window handle
7968 * [I] INT : item index
7974 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7976 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7977 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7978 BOOL bResult = FALSE;
7981 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
7983 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7987 /* rearrange with default alignment style */
7988 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7989 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7991 ListView_Arrange(hwnd, 0);
7995 /* get item bounding rectangle */
7996 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7997 InvalidateRect(hwnd, &rc, TRUE);
8006 * Creates the listview control.
8009 * [I] HWND : window handle
8014 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
8016 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8017 UINT uView = lpcs->style & LVS_TYPEMASK;
8020 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
8022 /* initialize info pointer */
8023 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
8025 /* determine the type of structures to use */
8026 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
8027 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8029 /* initialize color information */
8030 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
8031 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
8032 infoPtr->clrTextBk = CLR_DEFAULT;
8034 /* set default values */
8035 infoPtr->hwndSelf = hwnd;
8036 infoPtr->uCallbackMask = 0;
8037 infoPtr->nFocusedItem = -1;
8038 infoPtr->nSelectionMark = -1;
8039 infoPtr->nHotItem = -1;
8040 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8041 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8042 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
8043 infoPtr->hwndEdit = 0;
8044 infoPtr->pedititem = NULL;
8045 infoPtr->nEditLabelItem = -1;
8046 infoPtr->bIsDrawing = FALSE;
8048 /* get default font (icon title) */
8049 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8050 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8051 infoPtr->hFont = infoPtr->hDefaultFont;
8052 LISTVIEW_SaveTextMetrics(hwnd);
8055 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
8056 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
8057 0, 0, 0, 0, hwnd, (HMENU)0,
8058 lpcs->hInstance, NULL);
8060 /* set header unicode format */
8061 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
8063 /* set header font */
8064 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
8067 if (uView == LVS_ICON)
8069 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8070 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8072 else if (uView == LVS_REPORT)
8074 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8076 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8080 /* set HDS_HIDDEN flag to hide the header bar */
8081 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
8082 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8086 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8087 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8091 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8092 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8095 /* display unsupported listview window styles */
8096 LISTVIEW_UnsupportedStyles(lpcs->style);
8098 /* allocate memory for the data structure */
8099 infoPtr->hdpaItems = DPA_Create(10);
8101 /* allocate memory for the selection ranges */
8102 infoPtr->hdpaSelectionRanges = DPA_Create(10);
8104 /* initialize size of items */
8105 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8106 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8108 /* initialize the hover time to -1(indicating the default system hover time) */
8109 infoPtr->dwHoverTime = -1;
8116 * Erases the background of the listview control.
8119 * [I] HWND : window handle
8120 * [I] WPARAM : device context handle
8121 * [I] LPARAM : not used
8127 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
8130 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8133 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8135 if (infoPtr->clrBk == CLR_NONE)
8137 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8142 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8143 GetClientRect(hwnd, &rc);
8144 FillRect((HDC)wParam, &rc, hBrush);
8145 DeleteObject(hBrush);
8153 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8155 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8157 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8159 if (infoPtr->clrBk != CLR_NONE)
8161 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8162 FillRect(hdc, rc, hBrush);
8163 DeleteObject(hBrush);
8169 * Retrieves the listview control font.
8172 * [I] HWND : window handle
8177 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8179 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8181 TRACE("(hwnd=%x)\n", hwnd);
8183 return infoPtr->hFont;
8188 * Performs vertical scrolling.
8191 * [I] HWND : window handle
8192 * [I] INT : scroll code
8193 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8195 * [I] HWND : scrollbar control window handle
8200 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8203 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8204 SCROLLINFO scrollInfo;
8206 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8207 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8209 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8211 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8212 scrollInfo.cbSize = sizeof(SCROLLINFO);
8213 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8215 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8217 INT nOldScrollPos = scrollInfo.nPos;
8218 switch (nScrollCode)
8221 if (scrollInfo.nPos > scrollInfo.nMin)
8226 if (scrollInfo.nPos < scrollInfo.nMax)
8231 if (scrollInfo.nPos > scrollInfo.nMin)
8233 if (scrollInfo.nPos >= scrollInfo.nPage)
8234 scrollInfo.nPos -= scrollInfo.nPage;
8236 scrollInfo.nPos = scrollInfo.nMin;
8241 if (scrollInfo.nPos < scrollInfo.nMax)
8243 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8244 scrollInfo.nPos += scrollInfo.nPage;
8246 scrollInfo.nPos = scrollInfo.nMax;
8250 case SB_THUMBPOSITION:
8252 scrollInfo.nPos = nCurrentPos;
8253 if (scrollInfo.nPos > scrollInfo.nMax)
8254 scrollInfo.nPos=scrollInfo.nMax;
8256 if (scrollInfo.nPos < scrollInfo.nMin)
8257 scrollInfo.nPos=scrollInfo.nMin;
8262 if (nOldScrollPos != scrollInfo.nPos)
8264 scrollInfo.fMask = SIF_POS;
8265 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8266 if (IsWindowVisible(infoPtr->hwndHeader))
8268 RECT rListview, rcHeader, rDest;
8269 GetClientRect(hwnd, &rListview);
8270 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8271 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8272 SubtractRect(&rDest, &rListview, &rcHeader);
8273 InvalidateRect(hwnd, &rDest, TRUE);
8276 InvalidateRect(hwnd, NULL, TRUE);
8285 * Performs horizontal scrolling.
8288 * [I] HWND : window handle
8289 * [I] INT : scroll code
8290 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8292 * [I] HWND : scrollbar control window handle
8297 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8300 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8301 SCROLLINFO scrollInfo;
8303 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8304 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8306 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8308 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8309 scrollInfo.cbSize = sizeof(SCROLLINFO);
8310 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8312 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8314 INT nOldScrollPos = scrollInfo.nPos;
8316 switch (nScrollCode)
8319 if (scrollInfo.nPos > scrollInfo.nMin)
8324 if (scrollInfo.nPos < scrollInfo.nMax)
8329 if (scrollInfo.nPos > scrollInfo.nMin)
8331 if (scrollInfo.nPos >= scrollInfo.nPage)
8332 scrollInfo.nPos -= scrollInfo.nPage;
8334 scrollInfo.nPos = scrollInfo.nMin;
8339 if (scrollInfo.nPos < scrollInfo.nMax)
8341 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8342 scrollInfo.nPos += scrollInfo.nPage;
8344 scrollInfo.nPos = scrollInfo.nMax;
8348 case SB_THUMBPOSITION:
8350 scrollInfo.nPos = nCurrentPos;
8352 if (scrollInfo.nPos > scrollInfo.nMax)
8353 scrollInfo.nPos=scrollInfo.nMax;
8355 if (scrollInfo.nPos < scrollInfo.nMin)
8356 scrollInfo.nPos=scrollInfo.nMin;
8360 if (nOldScrollPos != scrollInfo.nPos)
8362 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8363 scrollInfo.fMask = SIF_POS;
8364 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8365 if(uView == LVS_REPORT)
8367 scrollInfo.fMask = SIF_POS;
8368 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8369 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8371 InvalidateRect(hwnd, NULL, TRUE);
8378 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8380 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8381 INT gcWheelDelta = 0;
8382 UINT pulScrollLines = 3;
8383 SCROLLINFO scrollInfo;
8385 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8387 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8388 gcWheelDelta -= wheelDelta;
8390 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8391 scrollInfo.cbSize = sizeof(SCROLLINFO);
8392 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8399 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8400 * should be fixed in the future.
8402 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8403 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
8407 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8409 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8411 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8412 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8413 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8419 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8430 * [I] HWND : window handle
8431 * [I] INT : virtual key
8432 * [I] LONG : key data
8437 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8439 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8440 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8442 NMLVKEYDOWN nmKeyDown;
8444 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8446 /* send LVN_KEYDOWN notification */
8447 nmKeyDown.wVKey = nVirtualKey;
8448 nmKeyDown.flags = 0;
8449 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8451 switch (nVirtualKey)
8454 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8456 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8457 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8462 if (GETITEMCOUNT(infoPtr) > 0)
8467 if (GETITEMCOUNT(infoPtr) > 0)
8468 nItem = GETITEMCOUNT(infoPtr) - 1;
8472 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8476 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8480 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8484 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8488 if (uView == LVS_REPORT)
8489 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8491 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8492 * LISTVIEW_GetCountPerRow(hwnd);
8493 if(nItem < 0) nItem = 0;
8497 if (uView == LVS_REPORT)
8498 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8500 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8501 * LISTVIEW_GetCountPerRow(hwnd);
8502 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8506 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8508 if (LISTVIEW_KeySelection(hwnd, nItem))
8509 UpdateWindow(hwnd); /* update client area */
8520 * [I] HWND : window handle
8525 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8527 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8528 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8531 TRACE("(hwnd=%x)\n", hwnd);
8533 /* send NM_KILLFOCUS notification */
8534 hdr_notify(hwnd, NM_KILLFOCUS);
8536 /* set window focus flag */
8537 infoPtr->bFocus = FALSE;
8539 /* NEED drawing optimization ; redraw the selected items */
8540 if (uView & LVS_REPORT)
8542 nTop = LISTVIEW_GetTopIndex(hwnd);
8544 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8549 nBottom = GETITEMCOUNT(infoPtr);
8551 for (i = nTop; i<nBottom; i++)
8553 if (LISTVIEW_IsSelected(hwnd,i))
8556 rcItem.left = LVIR_BOUNDS;
8557 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8558 InvalidateRect(hwnd, &rcItem, FALSE);
8567 * Processes double click messages (left mouse button).
8570 * [I] HWND : window handle
8571 * [I] WORD : key flag
8572 * [I] WORD : x coordinate
8573 * [I] WORD : y coordinate
8578 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8581 LVHITTESTINFO htInfo;
8584 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8586 htInfo.pt.x = wPosX;
8587 htInfo.pt.y = wPosY;
8589 /* send NM_DBLCLK notification */
8590 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8591 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8593 nmlv.iItem = htInfo.iItem;
8594 nmlv.iSubItem = htInfo.iSubItem;
8601 nmlv.ptAction.x = wPosX;
8602 nmlv.ptAction.y = wPosY;
8603 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8606 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8607 if(nmlv.iItem != -1)
8608 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8615 * Processes mouse down messages (left mouse button).
8618 * [I] HWND : window handle
8619 * [I] WORD : key flag
8620 * [I] WORD : x coordinate
8621 * [I] WORD : y coordinate
8626 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8629 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8630 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8631 static BOOL bGroupSelect = TRUE;
8635 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8637 /* send NM_RELEASEDCAPTURE notification */
8638 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8640 if (infoPtr->bFocus == FALSE)
8643 /* set left button down flag */
8644 infoPtr->bLButtonDown = TRUE;
8646 ptPosition.x = wPosX;
8647 ptPosition.y = wPosY;
8648 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8649 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8651 if (lStyle & LVS_SINGLESEL)
8653 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8654 && infoPtr->nEditLabelItem == -1)
8655 infoPtr->nEditLabelItem = nItem;
8657 LISTVIEW_SetSelection(hwnd, nItem);
8661 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8664 LISTVIEW_AddGroupSelection(hwnd, nItem);
8666 LISTVIEW_AddSelection(hwnd, nItem);
8668 else if (wKey & MK_CONTROL)
8670 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8672 else if (wKey & MK_SHIFT)
8674 LISTVIEW_SetGroupSelection(hwnd, nItem);
8679 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8681 /* set selection (clears other pre-existing selections) */
8682 LISTVIEW_SetSelection(hwnd, nItem);
8684 if (was_selected && infoPtr->nEditLabelItem == -1)
8685 infoPtr->nEditLabelItem = nItem;
8691 /* remove all selections */
8692 LISTVIEW_RemoveAllSelections(hwnd);
8695 /* redraw if we could have possibly selected something */
8696 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8703 * Processes mouse up messages (left mouse button).
8706 * [I] HWND : window handle
8707 * [I] WORD : key flag
8708 * [I] WORD : x coordinate
8709 * [I] WORD : y coordinate
8714 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8717 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8719 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8721 if (infoPtr->bLButtonDown != FALSE)
8723 LVHITTESTINFO lvHitTestInfo;
8726 lvHitTestInfo.pt.x = wPosX;
8727 lvHitTestInfo.pt.y = wPosY;
8729 /* send NM_CLICK notification */
8730 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8731 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8733 nmlv.iItem = lvHitTestInfo.iItem;
8734 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8741 nmlv.ptAction.x = wPosX;
8742 nmlv.ptAction.y = wPosY;
8743 listview_notify(hwnd, NM_CLICK, &nmlv);
8745 /* set left button flag */
8746 infoPtr->bLButtonDown = FALSE;
8748 if(infoPtr->nEditLabelItem != -1)
8750 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL)
8751 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8752 infoPtr->nEditLabelItem = -1;
8761 * Creates the listview control (called before WM_CREATE).
8764 * [I] HWND : window handle
8765 * [I] WPARAM : unhandled
8766 * [I] LPARAM : widow creation info
8771 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8773 LISTVIEW_INFO *infoPtr;
8775 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8777 /* allocate memory for info structure */
8778 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8779 if (infoPtr == NULL)
8781 ERR("could not allocate info memory!\n");
8785 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8786 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
8788 ERR("pointer assignment error!\n");
8792 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8797 * Destroys the listview control (called after WM_DESTROY).
8800 * [I] HWND : window handle
8805 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8807 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8809 TRACE("(hwnd=%x)\n", hwnd);
8811 /* delete all items */
8812 LISTVIEW_DeleteAllItems(hwnd);
8814 /* destroy data structure */
8815 DPA_Destroy(infoPtr->hdpaItems);
8816 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8819 infoPtr->hFont = (HFONT)0;
8820 if (infoPtr->hDefaultFont)
8822 DeleteObject(infoPtr->hDefaultFont);
8825 /* free listview info pointer*/
8826 COMCTL32_Free(infoPtr);
8828 SetWindowLongW(hwnd, 0, 0);
8834 * Handles notifications from children.
8837 * [I] HWND : window handle
8838 * [I] INT : control identifier
8839 * [I] LPNMHDR : notification information
8844 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8846 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8848 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8850 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8852 /* handle notification from header control */
8853 if (lpnmh->code == HDN_ENDTRACKW)
8855 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8856 InvalidateRect(hwnd, NULL, TRUE);
8858 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
8860 /* Handle sorting by Header Column */
8863 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8865 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8866 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8868 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8870 /* Idealy this should be done in HDN_ENDTRACKA
8871 * but since SetItemBounds in Header.c is called after
8872 * the notification is sent, it is neccessary to handle the
8873 * update of the scroll bar here (Header.c works fine as it is,
8874 * no need to disturb it)
8876 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8877 LISTVIEW_UpdateScroll(hwnd);
8878 InvalidateRect(hwnd, NULL, TRUE);
8888 * Determines the type of structure to use.
8891 * [I] HWND : window handle of the sender
8892 * [I] HWND : listview window handle
8893 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8898 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8900 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8902 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8904 if (nCommand == NF_REQUERY)
8905 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8906 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8912 * Paints/Repaints the listview control.
8915 * [I] HWND : window handle
8916 * [I] HDC : device context handle
8921 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8925 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8929 hdc = BeginPaint(hwnd, &ps);
8930 LISTVIEW_Refresh(hwnd, hdc);
8931 EndPaint(hwnd, &ps);
8935 LISTVIEW_Refresh(hwnd, hdc);
8943 * Processes double click messages (right mouse button).
8946 * [I] HWND : window handle
8947 * [I] WORD : key flag
8948 * [I] WORD : x coordinate
8949 * [I] WORD : y coordinate
8954 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8957 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8959 /* send NM_RELEASEDCAPTURE notification */
8960 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8962 /* send NM_RDBLCLK notification */
8963 hdr_notify(hwnd, NM_RDBLCLK);
8970 * Processes mouse down messages (right mouse button).
8973 * [I] HWND : window handle
8974 * [I] WORD : key flag
8975 * [I] WORD : x coordinate
8976 * [I] WORD : y coordinate
8981 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8984 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8988 LVHITTESTINFO lvHitTestInfo;
8990 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8992 /* send NM_RELEASEDCAPTURE notification */
8993 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8995 /* make sure the listview control window has the focus */
8996 if (infoPtr->bFocus == FALSE)
8999 /* set right button down flag */
9000 infoPtr->bRButtonDown = TRUE;
9002 /* determine the index of the selected item */
9003 ptPosition.x = wPosX;
9004 ptPosition.y = wPosY;
9005 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
9006 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
9008 LISTVIEW_SetItemFocus(hwnd,nItem);
9009 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9010 !LISTVIEW_IsSelected(hwnd,nItem))
9011 LISTVIEW_SetSelection(hwnd, nItem);
9015 LISTVIEW_RemoveAllSelections(hwnd);
9018 lvHitTestInfo.pt.x = wPosX;
9019 lvHitTestInfo.pt.y = wPosY;
9021 /* Send NM_RClICK notification */
9022 ZeroMemory(&nmlv, sizeof(nmlv));
9023 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
9025 nmlv.iItem = lvHitTestInfo.iItem;
9026 nmlv.iSubItem = lvHitTestInfo.iSubItem;
9033 nmlv.ptAction.x = wPosX;
9034 nmlv.ptAction.y = wPosY;
9035 listview_notify(hwnd, NM_RCLICK, &nmlv);
9042 * Processes mouse up messages (right mouse button).
9045 * [I] HWND : window handle
9046 * [I] WORD : key flag
9047 * [I] WORD : x coordinate
9048 * [I] WORD : y coordinate
9053 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
9056 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9058 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9060 if (infoPtr->bRButtonDown)
9067 /* set button flag */
9068 infoPtr->bRButtonDown = FALSE;
9070 /* Change to screen coordinate for WM_CONTEXTMENU */
9071 ClientToScreen(hwnd, &pt);
9073 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9074 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9085 * [I] HWND : window handle
9086 * [I] HWND : window handle of previously focused window
9091 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9093 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9095 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9097 /* send NM_SETFOCUS notification */
9098 hdr_notify(hwnd, NM_SETFOCUS);
9100 /* set window focus flag */
9101 infoPtr->bFocus = TRUE;
9113 * [I] HWND : window handle
9114 * [I] HFONT : font handle
9115 * [I] WORD : redraw flag
9120 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9122 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9123 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9125 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9127 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9128 LISTVIEW_SaveTextMetrics(hwnd);
9130 if (uView == LVS_REPORT)
9132 /* set header font */
9133 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9134 MAKELPARAM(fRedraw, 0));
9137 /* invalidate listview control client area */
9138 InvalidateRect(hwnd, NULL, TRUE);
9140 if (fRedraw != FALSE)
9148 * Message handling for WM_SETREDRAW.
9149 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9152 * [I] HWND : window handle
9153 * [I] bRedraw: state of redraw flag
9156 * DefWinProc return value
9158 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9160 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9162 RedrawWindow(hwnd, NULL, 0,
9163 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9169 * Resizes the listview control. This function processes WM_SIZE
9170 * messages. At this time, the width and height are not used.
9173 * [I] HWND : window handle
9174 * [I] WORD : new width
9175 * [I] WORD : new height
9180 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9182 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9183 UINT uView = lStyle & LVS_TYPEMASK;
9185 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9187 if (LISTVIEW_UpdateSize(hwnd))
9189 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9191 if (lStyle & LVS_ALIGNLEFT)
9192 LISTVIEW_AlignLeft(hwnd);
9194 LISTVIEW_AlignTop(hwnd);
9197 LISTVIEW_UpdateScroll(hwnd);
9199 /* invalidate client area + erase background */
9200 InvalidateRect(hwnd, NULL, TRUE);
9208 * Sets the size information.
9211 * [I] HWND : window handle
9214 * Zero if no size change
9217 static BOOL LISTVIEW_UpdateSize(HWND hwnd)
9219 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9220 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9221 UINT uView = lStyle & LVS_TYPEMASK;
9225 TRACE("(hwnd=%x)\n", hwnd);
9227 GetClientRect(hwnd, &rcList);
9228 CopyRect(&rcOld,&(infoPtr->rcList));
9229 infoPtr->rcList.left = 0;
9230 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9231 infoPtr->rcList.top = 0;
9232 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9234 if (uView == LVS_LIST)
9236 if (lStyle & WS_HSCROLL)
9238 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9239 if (infoPtr->rcList.bottom > nHScrollHeight)
9240 infoPtr->rcList.bottom -= nHScrollHeight;
9243 else if (uView == LVS_REPORT)
9250 Header_Layout(infoPtr->hwndHeader, &hl);
9252 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9254 if (!(LVS_NOCOLUMNHEADER & lStyle))
9255 infoPtr->rcList.top = max(wp.cy, 0);
9257 return (EqualRect(&rcOld,&(infoPtr->rcList)));
9262 * Processes WM_STYLECHANGED messages.
9265 * [I] HWND : window handle
9266 * [I] WPARAM : window style type (normal or extended)
9267 * [I] LPSTYLESTRUCT : window style information
9272 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9275 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9276 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9277 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9278 RECT rcList = infoPtr->rcList;
9280 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
9281 hwnd, wStyleType, lpss);
9283 if (wStyleType == GWL_STYLE)
9285 if (uOldView == LVS_REPORT)
9286 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9288 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9289 ((lpss->styleNew & WS_HSCROLL) == 0))
9290 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9292 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9293 ((lpss->styleNew & WS_VSCROLL) == 0))
9294 ShowScrollBar(hwnd, SB_VERT, FALSE);
9296 if (uNewView == LVS_ICON)
9298 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9299 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9300 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9301 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9302 if (lpss->styleNew & LVS_ALIGNLEFT)
9303 LISTVIEW_AlignLeft(hwnd);
9305 LISTVIEW_AlignTop(hwnd);
9307 else if (uNewView == LVS_REPORT)
9314 Header_Layout(infoPtr->hwndHeader, &hl);
9315 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9317 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9318 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9320 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9321 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9322 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9323 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9325 else if (uNewView == LVS_LIST)
9327 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9328 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9329 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9330 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9334 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9335 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9336 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9337 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9338 if (lpss->styleNew & LVS_ALIGNLEFT)
9339 LISTVIEW_AlignLeft(hwnd);
9341 LISTVIEW_AlignTop(hwnd);
9344 /* update the size of the client area */
9345 LISTVIEW_UpdateSize(hwnd);
9347 /* add scrollbars if needed */
9348 LISTVIEW_UpdateScroll(hwnd);
9350 /* invalidate client area + erase background */
9351 InvalidateRect(hwnd, NULL, TRUE);
9353 /* print the list of unsupported window styles */
9354 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9357 /* If they change the view and we have an active edit control
9358 we will need to kill the control since the redraw will
9359 misplace the edit control.
9361 if (infoPtr->hwndEdit &&
9362 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9363 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9365 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9373 * Window procedure of the listview control.
9376 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9379 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9380 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9381 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9384 case LVM_APPROXIMATEVIEWRECT:
9385 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9386 LOWORD(lParam), HIWORD(lParam));
9388 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9390 /* case LVM_CREATEDRAGIMAGE: */
9392 case LVM_DELETEALLITEMS:
9393 return LISTVIEW_DeleteAllItems(hwnd);
9395 case LVM_DELETECOLUMN:
9396 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9398 case LVM_DELETEITEM:
9399 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9401 case LVM_EDITLABELW:
9402 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9404 case LVM_EDITLABELA:
9405 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9407 case LVM_ENSUREVISIBLE:
9408 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9411 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9414 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9416 case LVM_GETBKCOLOR:
9417 return LISTVIEW_GetBkColor(hwnd);
9419 /* case LVM_GETBKIMAGE: */
9421 case LVM_GETCALLBACKMASK:
9422 return LISTVIEW_GetCallbackMask(hwnd);
9424 case LVM_GETCOLUMNA:
9425 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9427 case LVM_GETCOLUMNW:
9428 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9430 case LVM_GETCOLUMNORDERARRAY:
9431 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9433 case LVM_GETCOLUMNWIDTH:
9434 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9436 case LVM_GETCOUNTPERPAGE:
9437 return LISTVIEW_GetCountPerPage(hwnd);
9439 case LVM_GETEDITCONTROL:
9440 return LISTVIEW_GetEditControl(hwnd);
9442 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9443 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9446 return LISTVIEW_GetHeader(hwnd);
9448 case LVM_GETHOTCURSOR:
9449 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9452 case LVM_GETHOTITEM:
9453 return LISTVIEW_GetHotItem(hwnd);
9455 case LVM_GETHOVERTIME:
9456 return LISTVIEW_GetHoverTime(hwnd);
9458 case LVM_GETIMAGELIST:
9459 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9461 case LVM_GETISEARCHSTRINGA:
9462 case LVM_GETISEARCHSTRINGW:
9463 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9467 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9470 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9472 case LVM_GETITEMCOUNT:
9473 return LISTVIEW_GetItemCount(hwnd);
9475 case LVM_GETITEMPOSITION:
9476 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9478 case LVM_GETITEMRECT:
9479 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9481 case LVM_GETITEMSPACING:
9482 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9484 case LVM_GETITEMSTATE:
9485 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9487 case LVM_GETITEMTEXTA:
9488 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9490 case LVM_GETITEMTEXTW:
9491 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9493 case LVM_GETNEXTITEM:
9494 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9496 case LVM_GETNUMBEROFWORKAREAS:
9497 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9501 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9503 case LVM_GETSELECTEDCOUNT:
9504 return LISTVIEW_GetSelectedCount(hwnd);
9506 case LVM_GETSELECTIONMARK:
9507 return LISTVIEW_GetSelectionMark(hwnd);
9509 case LVM_GETSTRINGWIDTHA:
9510 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9512 case LVM_GETSTRINGWIDTHW:
9513 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9515 case LVM_GETSUBITEMRECT:
9516 return LISTVIEW_GetSubItemRect(hwnd, (UINT)wParam, ((LPRECT)lParam)->top,
9517 ((LPRECT)lParam)->left, (LPRECT)lParam);
9519 case LVM_GETTEXTBKCOLOR:
9520 return LISTVIEW_GetTextBkColor(hwnd);
9522 case LVM_GETTEXTCOLOR:
9523 return LISTVIEW_GetTextColor(hwnd);
9525 case LVM_GETTOOLTIPS:
9526 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9529 case LVM_GETTOPINDEX:
9530 return LISTVIEW_GetTopIndex(hwnd);
9532 /*case LVM_GETUNICODEFORMAT:
9533 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9536 case LVM_GETVIEWRECT:
9537 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9539 case LVM_GETWORKAREAS:
9540 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9544 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9546 case LVM_INSERTCOLUMNA:
9547 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9549 case LVM_INSERTCOLUMNW:
9550 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9552 case LVM_INSERTITEMA:
9553 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9555 case LVM_INSERTITEMW:
9556 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9558 case LVM_REDRAWITEMS:
9559 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9561 /* case LVM_SCROLL: */
9562 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9564 case LVM_SETBKCOLOR:
9565 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9567 /* case LVM_SETBKIMAGE: */
9569 case LVM_SETCALLBACKMASK:
9570 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9572 case LVM_SETCOLUMNA:
9573 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9575 case LVM_SETCOLUMNW:
9576 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9578 case LVM_SETCOLUMNORDERARRAY:
9579 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9581 case LVM_SETCOLUMNWIDTH:
9582 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9584 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9585 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9587 /* case LVM_SETHOTCURSOR: */
9589 case LVM_SETHOTITEM:
9590 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9592 case LVM_SETHOVERTIME:
9593 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9595 case LVM_SETICONSPACING:
9596 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9598 case LVM_SETIMAGELIST:
9599 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9602 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9605 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9607 case LVM_SETITEMCOUNT:
9608 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9610 case LVM_SETITEMPOSITION:
9611 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9612 (INT)HIWORD(lParam));
9614 case LVM_SETITEMPOSITION32:
9615 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9616 ((POINT*)lParam)->y);
9618 case LVM_SETITEMSTATE:
9619 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9621 case LVM_SETITEMTEXTA:
9622 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9624 case LVM_SETITEMTEXTW:
9625 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9627 case LVM_SETSELECTIONMARK:
9628 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9630 case LVM_SETTEXTBKCOLOR:
9631 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9633 case LVM_SETTEXTCOLOR:
9634 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9636 /* case LVM_SETTOOLTIPS: */
9637 /* case LVM_SETUNICODEFORMAT: */
9638 /* case LVM_SETWORKAREAS: */
9641 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9643 /* case LVM_SUBITEMHITTEST: */
9646 return LISTVIEW_Update(hwnd, (INT)wParam);
9649 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9652 return LISTVIEW_Command(hwnd, wParam, lParam);
9655 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9658 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9661 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9664 return LISTVIEW_GetFont(hwnd);
9667 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9668 (INT)HIWORD(wParam), (HWND)lParam);
9671 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9674 return LISTVIEW_KillFocus(hwnd);
9676 case WM_LBUTTONDBLCLK:
9677 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9680 case WM_LBUTTONDOWN:
9681 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9684 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9687 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9690 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9693 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9696 return LISTVIEW_NCDestroy(hwnd);
9699 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9701 case WM_NOTIFYFORMAT:
9702 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9705 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9707 case WM_RBUTTONDBLCLK:
9708 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9711 case WM_RBUTTONDOWN:
9712 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9716 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9720 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9723 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9726 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9729 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9731 case WM_STYLECHANGED:
9732 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9734 /* case WM_TIMER: */
9737 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9738 (INT)HIWORD(wParam), (HWND)lParam);
9741 if (wParam & (MK_SHIFT | MK_CONTROL))
9742 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9743 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9745 /* case WM_WININICHANGE: */
9748 if (uMsg >= WM_USER)
9750 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9754 /* call default window procedure */
9755 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9763 * Registers the window class.
9771 VOID LISTVIEW_Register(void)
9775 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9776 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9777 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9778 wndClass.cbClsExtra = 0;
9779 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9780 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9781 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9782 wndClass.lpszClassName = WC_LISTVIEWW;
9783 RegisterClassW(&wndClass);
9788 * Unregisters the window class.
9796 VOID LISTVIEW_Unregister(void)
9798 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9803 * Handle any WM_COMMAND messages
9809 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9811 switch (HIWORD(wParam))
9816 * Adjust the edit window size
9819 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9820 HDC hdc = GetDC(infoPtr->hwndEdit);
9821 HFONT hFont, hOldFont = 0;
9826 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9827 GetWindowRect(infoPtr->hwndEdit, &rect);
9829 /* Select font to get the right dimension of the string */
9830 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9833 hOldFont = SelectObject(hdc, hFont);
9836 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9838 TEXTMETRICW textMetric;
9840 /* Add Extra spacing for the next character */
9841 GetTextMetricsW(hdc, &textMetric);
9842 sz.cx += (textMetric.tmMaxCharWidth * 2);
9850 rect.bottom - rect.top,
9851 SWP_DRAWFRAME|SWP_NOMOVE);
9854 SelectObject(hdc, hOldFont);
9856 ReleaseDC(hwnd, hdc);
9862 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9871 * Subclassed edit control windproc function
9877 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9878 WPARAM wParam, LPARAM lParam, BOOL isW)
9880 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9881 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9882 static BOOL bIgnoreKillFocus = FALSE;
9883 BOOL cancel = FALSE;
9885 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9886 hwnd, uMsg, wParam, lParam, isW);
9891 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9894 if(bIgnoreKillFocus) return TRUE;
9899 WNDPROC editProc = einfo->EditWndProc;
9900 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9901 COMCTL32_Free(einfo);
9902 infoPtr->pedititem = NULL;
9903 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9907 if (VK_ESCAPE == (INT)wParam)
9912 else if (VK_RETURN == (INT)wParam)
9916 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9919 if (einfo->EditLblCb)
9921 LPWSTR buffer = NULL;
9925 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9929 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9931 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9932 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9936 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9937 /* eg. Using a messagebox */
9938 bIgnoreKillFocus = TRUE;
9939 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9941 if (buffer) COMCTL32_Free(buffer);
9943 einfo->EditLblCb = NULL;
9944 bIgnoreKillFocus = FALSE;
9947 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9953 * Subclassed edit control windproc function
9959 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9961 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9966 * Subclassed edit control windproc function
9972 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9974 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9979 * Creates a subclassed edit cotrol
9985 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
9986 INT width, INT height, HWND parent, HINSTANCE hinst,
9987 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
9989 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
9990 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9995 TEXTMETRICW textMetric;
9997 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
9999 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
10002 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
10003 hdc = GetDC(parent);
10005 /* Select the font to get appropriate metric dimensions */
10006 if(infoPtr->hFont != 0)
10007 hOldFont = SelectObject(hdc, infoPtr->hFont);
10009 /*Get String Lenght in pixels */
10010 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10012 /*Add Extra spacing for the next character */
10013 GetTextMetricsW(hdc, &textMetric);
10014 sz.cx += (textMetric.tmMaxCharWidth * 2);
10016 if(infoPtr->hFont != 0)
10017 SelectObject(hdc, hOldFont);
10019 ReleaseDC(parent, hdc);
10021 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10023 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10027 COMCTL32_Free(infoPtr->pedititem);
10031 infoPtr->pedititem->param = param;
10032 infoPtr->pedititem->EditLblCb = EditLblCb;
10033 infoPtr->pedititem->EditWndProc = (WNDPROC)
10034 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
10035 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
10037 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);