* Copyright 2000 Jason Mawdsley
* Copyright 2001 CodeWeavers Inc.
* Copyright 2002 Dimitrie O. Paun
- * Copyright 2009-2011 Nikolay Sivov
+ * Copyright 2009-2014 Nikolay Sivov
* Copyright 2009 Owen Rudge for CodeWeavers
+ * Copyright 2012-2013 Daniel Jelinski
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* -- LVA_SNAPTOGRID not implemented
* -- LISTVIEW_ApproximateViewRect partially implemented
* -- LISTVIEW_SetColumnWidth ignores header images & bitmap
- * -- LISTVIEW_SetIconSpacing is incomplete
* -- LISTVIEW_StyleChanged doesn't handle some changes too well
*
* Speedups
* States
* -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
* -- LVIS_DROPHILITED
- * -- LVIS_OVERLAYMASK
*
* Styles
* -- LVS_NOLABELWRAP
* -- LVN_GETINFOTIP
* -- LVN_HOTTRACK
* -- LVN_SETDISPINFO
- * -- LVN_BEGINRDRAG
*
* Messages:
* -- LVM_ENABLEGROUPVIEW
*
* Functions:
* -- LVGroupComparE
- *
- * Known differences in message stream from native control (not known if
- * these differences cause problems):
- * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
- * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
- * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
- * processing for "USEDOUBLECLICKTIME".
*/
-#include "config.h"
-#include "wine/port.h"
-
-#include <assert.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-#include "windef.h"
-#include "winbase.h"
-#include "winnt.h"
-#include "wingdi.h"
-#include "winuser.h"
-#include "winnls.h"
-#include "commctrl.h"
#include "comctl32.h"
-#include "uxtheme.h"
-#include "wine/debug.h"
-#include "wine/unicode.h"
+#include <stdio.h>
WINE_DEFAULT_DEBUG_CHANNEL(listview);
HIMAGELIST himlSmall;
HIMAGELIST himlState;
SIZE iconSize;
+ BOOL autoSpacing;
SIZE iconSpacing;
SIZE iconStateSize;
POINT currIconPos; /* this is the position next icon will be placed */
/* mouse operation */
BOOL bLButtonDown;
- BOOL bRButtonDown;
BOOL bDragging;
POINT ptClickPos; /* point where the user clicked */
INT nLButtonDownItem; /* tracks item to reset multiselection on WM_LBUTTONUP */
DWORD dwHoverTime;
HCURSOR hHotCursor;
+ INT cWheelRemainder;
/* keyboard operation */
DWORD lastKeyPressTimestamp;
WCHAR szSearchParam[ MAX_PATH ];
/* painting */
- DWORD cditemmode; /* Keep the custom draw flags for an item/row */
BOOL bIsDrawing; /* Drawing in progress */
INT nMeasureItemHeight; /* WM_MEASUREITEM result */
BOOL bRedraw; /* WM_SETREDRAW switch */
static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
{
- int res;
-
n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
- res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
- return res ? res - sizeof(WCHAR) : res;
+ return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n) - CSTR_EQUAL;
}
/******** Debugging functions *****************************************/
}
/* forwards header notifications to listview parent */
-static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
+static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, NMHEADERW *lpnmhW)
{
- NMHEADERA nmhA;
- HDITEMA hditema;
- HD_TEXTFILTERA textfilter;
- LPSTR text = NULL, filter = NULL;
+ LPCWSTR text = NULL, filter = NULL;
LRESULT ret;
+ NMHEADERA *lpnmh = (NMHEADERA*) lpnmhW;
/* on unicode format exit earlier */
if (infoPtr->notifyFormat == NFR_UNICODE)
/* header always supplies unicode notifications,
all we have to do is to convert strings to ANSI */
- nmhA = *(const NMHEADERA*)lpnmh;
if (lpnmh->pitem)
{
- hditema = *(HDITEMA*)lpnmh->pitem;
- nmhA.pitem = &hditema;
/* convert item text */
if (lpnmh->pitem->mask & HDI_TEXT)
{
- hditema.pszText = NULL;
- Str_SetPtrWtoA(&hditema.pszText, lpnmh->pitem->pszText);
- text = hditema.pszText;
+ text = (LPCWSTR)lpnmh->pitem->pszText;
+ lpnmh->pitem->pszText = NULL;
+ Str_SetPtrWtoA(&lpnmh->pitem->pszText, text);
}
/* convert filter text */
if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
lpnmh->pitem->pvFilter)
{
- hditema.pvFilter = &textfilter;
- textfilter = *(HD_TEXTFILTERA*)(lpnmh->pitem->pvFilter);
- textfilter.pszText = NULL;
- Str_SetPtrWtoA(&textfilter.pszText, ((HD_TEXTFILTERW*)lpnmh->pitem->pvFilter)->pszText);
- filter = textfilter.pszText;
+ filter = (LPCWSTR)((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText;
+ ((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText = NULL;
+ Str_SetPtrWtoA(&((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText, filter);
}
}
- nmhA.hdr.code = get_ansi_notification(lpnmh->hdr.code);
+ lpnmh->hdr.code = get_ansi_notification(lpnmh->hdr.code);
- ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhA.hdr.idFrom,
- (LPARAM)&nmhA);
+ ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmh->hdr.idFrom,
+ (LPARAM)lpnmh);
/* cleanup */
- Free(text);
- Free(filter);
+ if(text)
+ {
+ Free(lpnmh->pitem->pszText);
+ lpnmh->pitem->pszText = (LPSTR)text;
+ }
+ if(filter)
+ {
+ Free(((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText);
+ ((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText = (LPSTR)filter;
+ }
return ret;
}
return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
}
+/* Handles NM_DBLCLK, NM_CLICK, NM_RDBLCLK, NM_RCLICK. Only NM_RCLICK return value is used. */
static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
{
NMITEMACTIVATE nmia;
LVITEMW item;
HWND hwnd = infoPtr->hwndSelf;
+ LRESULT ret;
TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
ZeroMemory(&nmia, sizeof(nmia));
item.iItem = lvht->iItem;
item.iSubItem = 0;
if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
- notify_hdr(infoPtr, code, (LPNMHDR)&nmia);
- return IsWindow(hwnd);
+ ret = notify_hdr(infoPtr, code, (NMHDR*)&nmia);
+ return IsWindow(hwnd) && (code == NM_RCLICK ? !ret : TRUE);
}
static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
{
- if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
- lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
- if (lpnmlvcd->clrText == CLR_DEFAULT)
- lpnmlvcd->clrText = comctl32_color.clrWindowText;
+ COLORREF backcolor, textcolor;
/* apparently, for selected items, we have to override the returned values */
if (!SubItem)
}
}
+ backcolor = lpnmlvcd->clrTextBk;
+ textcolor = lpnmlvcd->clrText;
+
+ if (backcolor == CLR_DEFAULT)
+ backcolor = comctl32_color.clrWindow;
+ if (textcolor == CLR_DEFAULT)
+ textcolor = comctl32_color.clrWindowText;
+
/* Set the text attributes */
- if (lpnmlvcd->clrTextBk != CLR_NONE)
+ if (backcolor != CLR_NONE)
{
SetBkMode(hdc, OPAQUE);
- SetBkColor(hdc,lpnmlvcd->clrTextBk);
+ SetBkColor(hdc, backcolor);
}
else
SetBkMode(hdc, TRANSPARENT);
- SetTextColor(hdc, lpnmlvcd->clrText);
+ SetTextColor(hdc, textcolor);
}
static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
* ITERATOR DOCUMENTATION
*
* The iterator functions allow for easy, and convenient iteration
- * over items of interest in the list. Typically, you create a
+ * over items of interest in the list. Typically, you create an
* iterator, use it, and destroy it, as such:
* ITERATOR i;
*
/* set header font */
SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, TRUE);
+ /* set header image list */
+ if (infoPtr->himlSmall)
+ SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)infoPtr->himlSmall);
+
LISTVIEW_UpdateSize(infoPtr);
return 0;
static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
{
WCHAR buffer[MAX_PATH];
- INT endidx, startidx;
DWORD prevTime;
LVITEMW item;
+ int startidx;
INT nItem;
INT diff;
infoPtr->nSearchParamLength = 1;
}
- /* and search from the current position */
- nItem = -1;
- endidx = infoPtr->nItemCount;
-
/* should start from next after focused item, so next item that matches
will be selected, if there isn't any and focused matches it will be selected
on second search stage from beginning of the list */
if (infoPtr->nFocusedItem >= 0 && infoPtr->nItemCount > 1)
- startidx = infoPtr->nFocusedItem + 1;
+ {
+ /* with some accumulated search data available start with current focus, otherwise
+ it's excluded from search */
+ startidx = infoPtr->nSearchParamLength > 1 ? infoPtr->nFocusedItem : infoPtr->nFocusedItem + 1;
+ if (startidx == infoPtr->nItemCount) startidx = 0;
+ }
else
startidx = 0;
}
else
{
- INT i = startidx;
+ int i = startidx, endidx;
+
+ /* and search from the current position */
+ nItem = -1;
+ endidx = infoPtr->nItemCount;
/* first search in [startidx, endidx), on failure continue in [0, startidx) */
while (1)
item.cchTextMax = MAX_PATH;
if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
- if (lstrncmpiW(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength) == 0)
+ if (!lstrncmpiW(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength))
{
nItem = i;
break;
}
- else if (nItem == -1 && lstrncmpiW(item.pszText, infoPtr->szSearchParam, 1) == 0)
+ /* this is used to find first char match when search string is not available yet,
+ otherwise every WM_CHAR will search to next item by first char, ignoring that we're
+ already waiting for user to complete a string */
+ else if (nItem == -1 && infoPtr->nSearchParamLength == 1 && !lstrncmpiW(item.pszText, infoPtr->szSearchParam, 1))
{
/* this would work but we must keep looking for a longer match */
nItem = i;
Icon.top = Box.top;
Icon.right = Icon.left;
if (infoPtr->himlSmall &&
- (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
+ (!lpColumnInfo || lpLVItem->iSubItem == 0 ||
((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
Icon.right += infoPtr->iconSize.cx;
Icon.bottom = Icon.top + infoPtr->iconSize.cy;
nItemHeight = infoPtr->iconSpacing.cy;
else
{
- nItemHeight = infoPtr->ntmHeight;
- if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
- nItemHeight++;
+ nItemHeight = infoPtr->ntmHeight;
if (infoPtr->himlState)
nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
if (infoPtr->himlSmall)
nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
- if (infoPtr->himlState || infoPtr->himlSmall)
- nItemHeight += HEIGHT_PADDING;
+ nItemHeight += HEIGHT_PADDING;
if (infoPtr->nMeasureItemHeight > 0)
nItemHeight = infoPtr->nMeasureItemHeight;
}
return oldFocus != infoPtr->nFocusedItem;
}
-/* Helper function for LISTVIEW_ShiftIndices *only* */
static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
{
if (nShiftItem < nItem) return nShiftItem;
return min(nShiftItem, infoPtr->nItemCount - 1);
}
+/* This function updates focus index.
+
+Parameters:
+ focus : current focus index
+ item : index of item to be added/removed
+ direction : add/remove flag
+*/
+static void LISTVIEW_ShiftFocus(LISTVIEW_INFO *infoPtr, INT focus, INT item, INT direction)
+{
+ BOOL old_change = infoPtr->bDoChangeNotify;
+
+ infoPtr->bDoChangeNotify = FALSE;
+ focus = shift_item(infoPtr, focus, item, direction);
+ if (focus != infoPtr->nFocusedItem)
+ LISTVIEW_SetItemFocus(infoPtr, focus);
+ infoPtr->bDoChangeNotify = old_change;
+}
+
/**
* DESCRIPTION:
* Updates the various indices after an item has been inserted or deleted.
*/
static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
{
- INT nNewFocus;
- BOOL bOldChange;
-
- /* temporarily disable change notification while shifting items */
- bOldChange = infoPtr->bDoChangeNotify;
- infoPtr->bDoChangeNotify = FALSE;
-
- TRACE("Shifting %iu, %i steps\n", nItem, direction);
+ TRACE("Shifting %i, %i steps\n", nItem, direction);
ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
-
assert(abs(direction) == 1);
-
infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
- nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
- if (nNewFocus != infoPtr->nFocusedItem)
- LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
-
/* But we are not supposed to modify nHotItem! */
-
- infoPtr->bDoChangeNotify = bOldChange;
}
-
/**
* DESCRIPTION:
* Adds a block of selections.
ZeroMemory(&nmlv, sizeof(nmlv));
nmlv.iFrom = nFirst;
nmlv.iTo = nLast;
- nmlv.uNewState = 0;
- nmlv.uOldState = item.state;
+ nmlv.uOldState = 0;
+ nmlv.uNewState = item.state;
notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
if (!IsWindow(hwndSelf))
POINT ptItem;
rcItem.left = LVIR_BOUNDS;
- if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
+ if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) {
+ ranges_destroy (selection);
+ return;
+ }
rcSelMark.left = LVIR_BOUNDS;
- if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
+ if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) {
+ ranges_destroy (selection);
+ return;
+ }
UnionRect(&rcSel, &rcItem, &rcSelMark);
iterator_frameditems(&i, infoPtr, &rcSel);
while(iterator_next(&i))
static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
{
/* FIXME: pass in the state */
- WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
- WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
+ WORD wShift = GetKeyState(VK_SHIFT) & 0x8000;
+ WORD wCtrl = GetKeyState(VK_CONTROL) & 0x8000;
BOOL bResult = FALSE;
TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
}
iterator_destroy(&new_elems);
- LISTVIEW_InvalidateRect(infoPtr, &rect);
+ LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
}
/***
*/
static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
{
+ LVHITTESTINFO ht;
+ RECT rect;
+ POINT pt;
+
if (!(fwKeys & MK_LBUTTON))
infoPtr->bLButtonDown = FALSE;
if (infoPtr->bLButtonDown)
{
- POINT tmp;
- RECT rect;
- LVHITTESTINFO lvHitTestInfo;
- WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
- WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
+ rect.left = rect.right = infoPtr->ptClickPos.x;
+ rect.top = rect.bottom = infoPtr->ptClickPos.y;
+
+ InflateRect(&rect, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
+ }
+ if (infoPtr->bLButtonDown)
+ {
if (infoPtr->bMarqueeSelect)
{
POINT coords_orig;
return 0;
}
- rect.left = infoPtr->ptClickPos.x - wDragWidth;
- rect.right = infoPtr->ptClickPos.x + wDragWidth;
- rect.top = infoPtr->ptClickPos.y - wDragHeight;
- rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
-
- tmp.x = x;
- tmp.y = y;
+ pt.x = x;
+ pt.y = y;
- lvHitTestInfo.pt = tmp;
- LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
+ ht.pt = pt;
+ LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
/* reset item marker */
- if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem)
+ if (infoPtr->nLButtonDownItem != ht.iItem)
infoPtr->nLButtonDownItem = -1;
- if (!PtInRect(&rect, tmp))
+ if (!PtInRect(&rect, pt))
{
/* this path covers the following:
1. WM_LBUTTONDOWN over selected item (sets focus on it)
if (!infoPtr->bDragging)
{
- lvHitTestInfo.pt = infoPtr->ptClickPos;
- LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
+ ht.pt = infoPtr->ptClickPos;
+ LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
/* If the click is outside the range of an item, begin a
highlight. If not, begin an item drag. */
- if (lvHitTestInfo.iItem == -1)
+ if (ht.iItem == -1)
{
NMHDR hdr;
NMLISTVIEW nmlv;
ZeroMemory(&nmlv, sizeof(nmlv));
- nmlv.iItem = lvHitTestInfo.iItem;
+ nmlv.iItem = ht.iItem;
nmlv.ptAction = infoPtr->ptClickPos;
notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
/***
* DESCRIPTION:
- * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
+ * Helper for LISTVIEW_SetItemT and LISTVIEW_InsertItemT: sets item attributes.
*
* PARAMETER(S):
* [I] infoPtr : valid pointer to the listview structure
if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
uChanged |= LVIF_TEXT;
- TRACE("uChanged=0x%x\n", uChanged);
- if (!uChanged) return TRUE;
- *bChanged = TRUE;
+ TRACE("change mask=0x%x\n", uChanged);
- ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
+ memset(&nmlv, 0, sizeof(NMLISTVIEW));
nmlv.iItem = lpLVItem->iItem;
nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
nmlv.uOldState = item.state;
- nmlv.uChanged = uChanged;
+ nmlv.uChanged = uChanged ? uChanged : lpLVItem->mask;
nmlv.lParam = item.lParam;
-
- /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
- /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
- /* are enabled */
+
+ /* Send LVN_ITEMCHANGING notification, if the item is not being inserted
+ and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications
+ are enabled. Even nothing really changed we still need to send this,
+ in this case uChanged mask is just set to passed item mask. */
if(lpItem && !isNew && infoPtr->bDoChangeNotify)
{
HWND hwndSelf = infoPtr->hwndSelf;
return FALSE;
}
+ /* When item is inserted we need to shift existing focus index if new item has lower index. */
+ if (isNew && (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) &&
+ /* this means we won't hit a focus change path later */
+ ((uChanged & LVIF_STATE) == 0 || (!(lpLVItem->state & LVIS_FOCUSED) && (infoPtr->nFocusedItem != lpLVItem->iItem))))
+ {
+ if (infoPtr->nFocusedItem != -1 && (lpLVItem->iItem <= infoPtr->nFocusedItem))
+ infoPtr->nFocusedItem++;
+ }
+
+ if (!uChanged) return TRUE;
+ *bChanged = TRUE;
+
/* copy information */
if (lpLVItem->mask & LVIF_TEXT)
textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
{
ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
}
- /* if we are asked to change focus, and we manage it, do it */
+ /* If we are asked to change focus, and we manage it, do it.
+ It's important to have all new item data stored at this point,
+ because changing existing focus could result in a redrawing operation,
+ which in turn could ask for disp data, application should see all data
+ for inserted item when processing LVN_GETDISPINFO.
+
+ The way this works application will see nested item change notifications -
+ changed item notifications interrupted by ones from item losing focus. */
if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
{
if (lpLVItem->state & LVIS_FOCUSED)
{
+ /* update selection mark */
+ if (infoPtr->nFocusedItem == -1 && infoPtr->nSelectionMark == -1)
+ infoPtr->nSelectionMark = lpLVItem->iItem;
+
if (infoPtr->nFocusedItem != -1)
{
/* remove current focus */
/* if we're inserting the item, we're done */
if (isNew) return TRUE;
-
+
/* send LVN_ITEMCHANGED notification */
if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
}
-/***
- * DESCRIPTION:
- * Draws an item.
- *
- * PARAMETER(S):
- * [I] infoPtr : valid pointer to the listview structure
- * [I] hdc : device context handle
- * [I] nItem : item index
- * [I] nSubItem : subitem index
- * [I] pos : item position in client coordinates
- * [I] cdmode : custom draw mode
- *
- * RETURN:
- * Success: TRUE
- * Failure: FALSE
- */
-static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
+/* Draw main item or subitem */
+static void LISTVIEW_DrawItemPart(LISTVIEW_INFO *infoPtr, LVITEMW *item, const NMLVCUSTOMDRAW *nmlvcd, const POINT *pos)
{
- UINT uFormat;
- WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
- static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
- DWORD cdsubitemmode = CDRF_DODEFAULT;
- LPRECT lprcFocus;
- RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
- NMLVCUSTOMDRAW nmlvcd;
+ RECT rcSelect, rcLabel, rcBox, rcStateIcon, rcIcon;
HIMAGELIST himl;
- LVITEMW lvItem;
- HFONT hOldFont;
-
- TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
-
- /* get information needed for drawing the item */
- lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
- if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
- if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
- lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT;
- lvItem.iItem = nItem;
- lvItem.iSubItem = nSubItem;
- lvItem.state = 0;
- lvItem.lParam = 0;
- lvItem.cchTextMax = DISP_TEXT_SIZE;
- lvItem.pszText = szDispText;
- if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
- if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
- lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
- if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
- TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
+ UINT format;
+ RECT *focus;
/* now check if we need to update the focus rectangle */
- lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
-
- if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
- LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
- OffsetRect(&rcBox, pos.x, pos.y);
- OffsetRect(&rcSelect, pos.x, pos.y);
- OffsetRect(&rcIcon, pos.x, pos.y);
- OffsetRect(&rcStateIcon, pos.x, pos.y);
- OffsetRect(&rcLabel, pos.x, pos.y);
+ focus = infoPtr->bFocus && (item->state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
+ if (!focus) item->state &= ~LVIS_FOCUSED;
+
+ LISTVIEW_GetItemMetrics(infoPtr, item, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
+ OffsetRect(&rcBox, pos->x, pos->y);
+ OffsetRect(&rcSelect, pos->x, pos->y);
+ OffsetRect(&rcIcon, pos->x, pos->y);
+ OffsetRect(&rcStateIcon, pos->x, pos->y);
+ OffsetRect(&rcLabel, pos->x, pos->y);
TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
- /* fill in the custom draw structure */
- customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
-
- hOldFont = GetCurrentObject(hdc, OBJ_FONT);
- if (nSubItem > 0) cdmode = infoPtr->cditemmode;
- if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
- if (cdmode & CDRF_NOTIFYITEMDRAW)
- cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
- if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
- if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
- /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
- if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
- {
- cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
- if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
- }
- if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
- prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
- else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
- prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
-
- /* in full row select, subitems, will just use main item's colors */
- if (nSubItem && infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
- nmlvcd.clrTextBk = CLR_NONE;
-
/* FIXME: temporary hack */
rcSelect.left = rcLabel.left;
- /* draw the selection background, if we're drawing the main item */
- if (nSubItem == 0)
- {
- /* in icon mode, the label rect is really what we want to draw the
- * background for */
- if (infoPtr->uView == LV_VIEW_ICON)
- rcSelect = rcLabel;
+ /* in icon mode, the label rect is really what we want to draw the
+ * background for */
+ /* in detail mode, we want to paint background for label rect when
+ * item is not selected or listview has full row select; otherwise paint
+ * background for text only */
+ if ( infoPtr->uView == LV_VIEW_ICON ||
+ (infoPtr->uView == LV_VIEW_DETAILS && (!(item->state & LVIS_SELECTED) ||
+ (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))))
+ rcSelect = rcLabel;
+
+ if (nmlvcd->clrTextBk != CLR_NONE)
+ ExtTextOutW(nmlvcd->nmcd.hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, NULL, 0, NULL);
+ if (item->state & LVIS_FOCUSED)
+ {
if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
{
/* we have to update left focus bound too if item isn't in leftmost column
if ((leftmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0)))
{
- INT Originx = pos.x - LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
+ INT Originx = pos->x - LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
INT index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
rcSelect.right = rcBox.right;
}
- if (nmlvcd.clrTextBk != CLR_NONE)
- ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, NULL, 0, NULL);
/* store new focus rectangle */
- if (infoPtr->nFocusedItem == nItem) infoPtr->rcFocus = rcSelect;
+ infoPtr->rcFocus = rcSelect;
}
/* state icons */
- if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
+ if (infoPtr->himlState && STATEIMAGEINDEX(item->state) && (item->iSubItem == 0))
{
- UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
- if (uStateImage)
+ UINT stateimage = STATEIMAGEINDEX(item->state);
+ if (stateimage)
{
- TRACE("uStateImage=%d\n", uStateImage);
- ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
- rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
+ TRACE("stateimage=%d\n", stateimage);
+ ImageList_Draw(infoPtr->himlState, stateimage-1, nmlvcd->nmcd.hdc, rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
}
}
/* item icons */
himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
- if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
+ if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon))
{
UINT style;
- TRACE("iImage=%d\n", lvItem.iImage);
+ TRACE("iImage=%d\n", item->iImage);
- if (lvItem.state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus)
+ if (item->state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus)
style = ILD_SELECTED;
else
style = ILD_NORMAL;
- ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
+ ImageList_DrawEx(himl, item->iImage, nmlvcd->nmcd.hdc, rcIcon.left, rcIcon.top,
rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk,
- lvItem.state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT,
- style);
+ item->state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT,
+ style | (item->state & LVIS_OVERLAYMASK));
}
/* Don't bother painting item being edited */
- if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
-
+ if (infoPtr->hwndEdit && item->iItem == infoPtr->nEditLabelItem && item->iSubItem == 0) return;
+
/* figure out the text drawing flags */
- uFormat = (infoPtr->uView == LV_VIEW_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
+ format = (infoPtr->uView == LV_VIEW_ICON ? (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
if (infoPtr->uView == LV_VIEW_ICON)
- uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
- else if (nSubItem)
+ format = (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
+ else if (item->iSubItem)
{
- switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
+ switch (LISTVIEW_GetColumnInfo(infoPtr, item->iSubItem)->fmt & LVCFMT_JUSTIFYMASK)
{
- case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
- case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
- default: uFormat |= DT_LEFT;
+ case LVCFMT_RIGHT: format |= DT_RIGHT; break;
+ case LVCFMT_CENTER: format |= DT_CENTER; break;
+ default: format |= DT_LEFT;
}
}
- if (!(uFormat & (DT_RIGHT | DT_CENTER)))
+ if (!(format & (DT_RIGHT | DT_CENTER)))
{
- if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
+ if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
else rcLabel.left += LABEL_HOR_PADDING;
}
- else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
+ else if (format & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
/* for GRIDLINES reduce the bottom so the text formats correctly */
if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
rcLabel.bottom--;
- if (!lprcFocus && (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTSHADOWTEXT))
- DrawShadowText(hdc, lvItem.pszText, -1, &rcLabel, uFormat, RGB(255, 255, 255), RGB(0, 0, 0), 2, 2);
+#ifdef __REACTOS__
+ if ((!(item->state & LVIS_SELECTED) || !infoPtr->bFocus) && (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTSHADOWTEXT))
+ DrawShadowText(nmlvcd->nmcd.hdc, item->pszText, -1, &rcLabel, format, RGB(255, 255, 255), RGB(0, 0, 0), 2, 2);
else
- DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
+#endif
+ DrawTextW(nmlvcd->nmcd.hdc, item->pszText, -1, &rcLabel, format);
+}
+
+/***
+ * DESCRIPTION:
+ * Draws an item.
+ *
+ * PARAMETER(S):
+ * [I] infoPtr : valid pointer to the listview structure
+ * [I] hdc : device context handle
+ * [I] nItem : item index
+ * [I] nSubItem : subitem index
+ * [I] pos : item position in client coordinates
+ * [I] cdmode : custom draw mode
+ *
+ * RETURN:
+ * Success: TRUE
+ * Failure: FALSE
+ */
+static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERATOR *subitems, POINT pos, DWORD cdmode)
+{
+ WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
+ static WCHAR callbackW[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
+ DWORD cdsubitemmode = CDRF_DODEFAULT;
+ RECT *focus, rcBox;
+ NMLVCUSTOMDRAW nmlvcd;
+ LVITEMW lvItem;
+
+ TRACE("(hdc=%p, nItem=%d, subitems=%p, pos=%s)\n", hdc, nItem, subitems, wine_dbgstr_point(&pos));
+
+ /* get information needed for drawing the item */
+ lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
+ if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
+ lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK;
+ lvItem.iItem = nItem;
+ lvItem.iSubItem = 0;
+ lvItem.state = 0;
+ lvItem.lParam = 0;
+ lvItem.cchTextMax = DISP_TEXT_SIZE;
+ lvItem.pszText = szDispText;
+ if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
+ if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW;
+ TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
+
+ /* now check if we need to update the focus rectangle */
+ focus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
+ if (!focus) lvItem.state &= ~LVIS_FOCUSED;
+
+ LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, NULL, NULL, NULL);
+ OffsetRect(&rcBox, pos.x, pos.y);
+
+ /* Full custom draw stage sequence looks like this:
+
+ LV_VIEW_DETAILS:
+
+ - CDDS_ITEMPREPAINT
+ - CDDS_ITEMPREPAINT|CDDS_SUBITEM | => sent n times, where n is number of subitems,
+ CDDS_ITEMPOSTPAINT|CDDS_SUBITEM | including item iself
+ - CDDS_ITEMPOSTPAINT
+
+ other styles:
+
+ - CDDS_ITEMPREPAINT
+ - CDDS_ITEMPOSTPAINT
+ */
+
+ /* fill in the custom draw structure */
+ customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
+ if (cdmode & CDRF_NOTIFYITEMDRAW)
+ cdsubitemmode = notify_customdraw(infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
+ if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
+
+ if (subitems)
+ {
+ while (iterator_next(subitems))
+ {
+ DWORD subitemstage = CDRF_DODEFAULT;
+
+ /* We need to query for each subitem, item's data (subitem == 0) is already here at this point */
+ if (subitems->nItem)
+ {
+ lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_INDENT;
+ lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK;
+ lvItem.iItem = nItem;
+ lvItem.iSubItem = subitems->nItem;
+ lvItem.state = 0;
+ lvItem.lParam = 0;
+ lvItem.cchTextMax = DISP_TEXT_SIZE;
+ lvItem.pszText = szDispText;
+ if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
+ if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
+ lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
+ if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW;
+ TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
+
+ /* update custom draw data */
+ LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &nmlvcd.nmcd.rc, NULL, NULL, NULL, NULL);
+ OffsetRect(&nmlvcd.nmcd.rc, pos.x, pos.y);
+ nmlvcd.iSubItem = subitems->nItem;
+ }
+
+ if (cdsubitemmode & CDRF_NOTIFYSUBITEMDRAW)
+ subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
+ else
+ {
+ nmlvcd.clrTextBk = infoPtr->clrTextBk;
+ nmlvcd.clrText = infoPtr->clrText;
+ }
+
+ if (subitems->nItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
+ prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
+ else if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
+ prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
+
+ if (!(subitemstage & CDRF_SKIPDEFAULT))
+ LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos);
+
+ if (subitemstage & CDRF_NOTIFYPOSTPAINT)
+ subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPOSTPAINT, &nmlvcd);
+ }
+ }
+ else
+ {
+ prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
+ LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos);
+ }
postpaint:
if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
- notify_postpaint(infoPtr, &nmlvcd);
- if (cdsubitemmode & CDRF_NEWFONT)
- SelectObject(hdc, hOldFont);
+ {
+ nmlvcd.iSubItem = 0;
+ notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
+ }
+
return TRUE;
}
/* iterate through the invalidated rows */
while(iterator_next(i))
{
+ RANGES subitems;
+ ITERATOR k;
+
+ SelectObject(hdc, infoPtr->hFont);
LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
Position.y += Origin.y;
+ subitems = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
+
/* iterate through the invalidated columns */
while(iterator_next(&j))
{
if (!RectVisible(hdc, &rcItem)) continue;
}
- LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
+ ranges_additem(subitems, j.nItem);
}
+
+ iterator_rangesitems(&k, subitems);
+ LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, &k, Position, cdmode);
+ iterator_destroy(&k);
}
iterator_destroy(&j);
}
itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
rcItem.left = infoPtr->rcList.left;
rcItem.right = infoPtr->rcList.right;
- rcItem.bottom = rcItem.top = Origin.y - 1;
- MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
- LineTo(hdc, rcItem.right, rcItem.top);
- for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
+ for(y = Origin.y > 1 ? Origin.y - 1 : itemheight - 1 + Origin.y % itemheight; y<=infoPtr->rcList.bottom; y+=itemheight)
{
rcItem.bottom = rcItem.top = y;
TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
SelectObject( hdc, hOldPen );
DeleteObject( hPen );
}
+ else
+ ranges_destroy(colRanges);
}
/***
while(iterator_prev(i))
{
+ SelectObject(hdc, infoPtr->hFont);
LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
Position.x += Origin.x;
Position.y += Origin.y;
- LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
+ LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, NULL, Position, cdmode);
}
}
*/
static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
{
- COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
+ COLORREF oldTextColor = 0, oldBkColor = 0;
NMLVCUSTOMDRAW nmlvcd;
HFONT hOldFont = 0;
DWORD cdmode;
hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
}
- /* FIXME: Shouldn't need to do this */
- oldClrTextBk = infoPtr->clrTextBk;
- oldClrText = infoPtr->clrText;
-
- infoPtr->cditemmode = CDRF_DODEFAULT;
-
GetClientRect(infoPtr->hwndSelf, &rcClient);
customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
- prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
-
- /* Use these colors to draw the items */
- infoPtr->clrTextBk = nmlvcd.clrTextBk;
- infoPtr->clrText = nmlvcd.clrText;
/* nothing to draw */
if(infoPtr->nItemCount == 0) goto enddraw;
if (cdmode & CDRF_NOTIFYPOSTPAINT)
notify_postpaint(infoPtr, &nmlvcd);
- infoPtr->clrTextBk = oldClrTextBk;
- infoPtr->clrText = oldClrText;
-
if(hbmp) {
BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
infoPtr->rcList.right - infoPtr->rcList.left,
POINT pos;
HDC hdc, hdcOrig;
HBITMAP hbmp, hOldbmp;
+ HFONT hOldFont;
HIMAGELIST dragList = 0;
TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
hdc = CreateCompatibleDC(hdcOrig);
hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
hOldbmp = SelectObject(hdc, hbmp);
+ hOldFont = SelectObject(hdc, infoPtr->hFont);
rcItem.left = rcItem.top = 0;
rcItem.right = size.cx;
FillRect(hdc, &rcItem, infoPtr->hBkBrush);
pos.x = pos.y = 0;
- if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
+ if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, NULL, pos, CDRF_DODEFAULT))
{
dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
SelectObject(hdc, hOldbmp);
else
SelectObject(hdc, hOldbmp);
+ SelectObject(hdc, hOldFont);
DeleteObject(hbmp);
DeleteDC(hdc);
ReleaseDC(infoPtr->hwndSelf, hdcOrig);
*/
static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
{
- NMLISTVIEW nmlv;
HDPA hdpaSubItems = NULL;
- BOOL bSuppress;
+ BOOL suppress = FALSE;
ITEMHDR *hdrItem;
ITEM_INFO *lpItem;
ITEM_ID *lpID;
SetRectEmpty(&infoPtr->rcFocus);
/* But we are supposed to leave nHotItem as is! */
-
/* send LVN_DELETEALLITEMS notification */
- ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
- nmlv.iItem = -1;
- bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
+ if (!(infoPtr->dwStyle & LVS_OWNERDATA) || !destroy)
+ {
+ NMLISTVIEW nmlv;
+
+ memset(&nmlv, 0, sizeof(NMLISTVIEW));
+ nmlv.iItem = -1;
+ suppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
+ }
for (i = infoPtr->nItemCount - 1; i >= 0; i--)
{
{
/* send LVN_DELETEITEM notification, if not suppressed
and if it is not a virtual listview */
- if (!bSuppress) notify_deleteitem(infoPtr, i);
+ if (!suppress) notify_deleteitem(infoPtr, i);
hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
lpItem = DPA_GetPtr(hdpaSubItems, 0);
/* free id struct */
TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
{
- TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
- ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
- &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
+ TRACE("Invalidating rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
+ InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE);
}
/* report has only that column, so we're done */
rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
OffsetRect(&rcScroll, Origin.x, Origin.y);
if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
- ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
- &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
+ InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE);
}
/***
{
LVITEMW item;
const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
+ INT focus = infoPtr->nFocusedItem;
TRACE("(nItem=%d)\n", nItem);
item.state = 0;
item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
LISTVIEW_SetItemState(infoPtr, nItem, &item);
-
+
/* send LVN_DELETEITEM notification. */
if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
infoPtr->nItemCount--;
LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
+ LISTVIEW_ShiftFocus(infoPtr, focus, nItem, -1);
/* now is the invalidation fun */
if (!is_icon)
{
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
+ lvItem.pszText = szDispText;
if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
if (lvItem.mask & LVIF_PARAM)
HDPA hdpaSubItems;
INT isubitem;
- TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
+ TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
return FALSE;
*
* NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
* not only those of the first column.
- * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
*
* RETURN:
* TRUE: success
* FALSE: failure
*/
-static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
+static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT item, LPRECT lprc)
{
- POINT Position, Origin;
- LVITEMW lvItem;
- INT nColumn;
+ RECT rect = { 0, 0, 0, 0 };
+ POINT origin;
+ INT y;
if (!lprc) return FALSE;
- nColumn = lprc->top;
-
- TRACE("(nItem=%d, nSubItem=%d, type=%d)\n", nItem, lprc->top, lprc->left);
- /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
+ TRACE("(item=%d, subitem=%d, type=%d)\n", item, lprc->top, lprc->left);
+ /* Subitem of '0' means item itself, and this works for all control view modes */
if (lprc->top == 0)
- return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
+ return LISTVIEW_GetItemRect(infoPtr, item, lprc);
if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
- /* special case for header items */
- if (nItem == -1)
- {
- if (lprc->left != LVIR_BOUNDS)
- {
- FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
- return FALSE;
- }
+ LISTVIEW_GetOrigin(infoPtr, &origin);
+ /* this works for any item index, no matter if it exists or not */
+ y = item * infoPtr->nItemHeight + origin.y;
- if (infoPtr->hwndHeader)
- return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
- else
- {
- memset(lprc, 0, sizeof(RECT));
- return TRUE;
- }
+ if (infoPtr->hwndHeader && SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)&rect))
+ {
+ rect.top = 0;
+ rect.bottom = infoPtr->nItemHeight;
+ }
+ else
+ {
+ /* Native implementation is broken for this case and garbage is left for left and right fields,
+ we zero them to get predictable output */
+ lprc->left = lprc->right = lprc->top = 0;
+ lprc->bottom = infoPtr->nItemHeight;
+ OffsetRect(lprc, origin.x, y);
+ TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
+ return TRUE;
}
- if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
- LISTVIEW_GetOrigin(infoPtr, &Origin);
-
- if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
-
- lvItem.mask = 0;
- lvItem.iItem = nItem;
- lvItem.iSubItem = nColumn;
-
- switch(lprc->left)
+ switch (lprc->left)
{
case LVIR_ICON:
- LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
- break;
+ {
+ /* it doesn't matter if main item actually has an icon, if imagelist is set icon width is returned */
+ if (infoPtr->himlSmall)
+ rect.right = rect.left + infoPtr->iconSize.cx;
+ else
+ rect.right = rect.left;
+ rect.bottom = rect.top + infoPtr->iconSize.cy;
+ break;
+ }
case LVIR_LABEL:
case LVIR_BOUNDS:
- LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
break;
default:
return FALSE;
}
- OffsetRect(lprc, Origin.x, Position.y);
+ OffsetRect(&rect, origin.x, y);
+ *lprc = rect;
TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
return TRUE;
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
POINT Origin, Position, opt;
+ BOOL is_fullrow;
LVITEMW lvItem;
ITERATOR i;
INT iItem;
TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
if (!PtInRect(&rcBounds, opt)) return -1;
+ /* That's a special case - row rectangle is used as item rectangle and
+ returned flags contain all item parts. */
+ is_fullrow = (infoPtr->uView == LV_VIEW_DETAILS) && ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || (infoPtr->dwStyle & LVS_OWNERDRAWFIXED));
+
if (PtInRect(&rcIcon, opt))
lpht->flags |= LVHT_ONITEMICON;
else if (PtInRect(&rcLabel, opt))
lpht->flags |= LVHT_ONITEMLABEL;
else if (infoPtr->himlState && PtInRect(&rcState, opt))
lpht->flags |= LVHT_ONITEMSTATEICON;
- /* special case for LVS_EX_FULLROWSELECT */
- if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
- !(lpht->flags & LVHT_ONITEM))
+ if (is_fullrow && !(lpht->flags & LVHT_ONITEM))
{
lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
}
lpht->flags &= ~LVHT_NOWHERE;
TRACE("lpht->flags=0x%x\n", lpht->flags);
- if (select && !(infoPtr->uView == LV_VIEW_DETAILS &&
- ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
- (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
+ if (select && !is_fullrow)
{
if (infoPtr->uView == LV_VIEW_DETAILS)
{
LVITEMW item;
HWND hwndSelf = infoPtr->hwndSelf;
- TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
+ TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
else
nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
- TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
+ TRACE("inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
if (nItem == -1) goto fail;
infoPtr->nItemCount++;
item.state &= ~LVIS_STATEIMAGEMASK;
item.state |= INDEXTOSTATEIMAGEMASK(1);
}
+
if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
/* make room for the position, if we are in the right mode */
goto undo;
}
}
-
+
/* send LVN_INSERTITEM notification */
- ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
+ memset(&nmlv, 0, sizeof(NMLISTVIEW));
nmlv.iItem = nItem;
nmlv.lParam = lpItem->lParam;
notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
undo:
LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
+ LISTVIEW_ShiftFocus(infoPtr, infoPtr->nFocusedItem, nItem, -1);
DPA_DeletePtr(infoPtr->hdpaItems, nItem);
infoPtr->nItemCount--;
fail:
* nearest number of pixels that are a whole line. Ex: if line height
* is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
* is passed, then the scroll will be 0. (per MSDN 7/2002)
- *
- * For: (per experimentation with native control and CSpy ListView)
- * LV_VIEW_ICON scrolling in any direction is allowed
- * LV_VIEW_SMALLICON scrolling in any direction is allowed
- * LV_VIEW_LIST dx=1 = 1 column (horizontal only)
- * but will only scroll 1 column per message
- * no matter what the value.
- * dy must be 0 or FALSE returned.
- * LV_VIEW_DETAILS dx=1 = 1 pixel
- * dy= see above
- *
*/
static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
{
INT max_cx = 0;
HDITEMW hdi;
- TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
+ TRACE("(nColumn=%d, cx=%d)\n", nColumn, cx);
/* set column width only if in report or list mode */
if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0;
lvItem.iSubItem = nColumn;
- lvItem.pszText = szDispText;
lvItem.cchTextMax = DISP_TEXT_SIZE;
for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
{
+ lvItem.pszText = szDispText;
if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
if (max_cx < nLabelWidth) max_cx = nLabelWidth;
*/
static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
{
+ INT iconWidth = 0, iconHeight = 0;
DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
TRACE("requested=(%d,%d)\n", cx, cy);
-
- /* this is supported only for LVS_ICON style */
- if (infoPtr->uView != LV_VIEW_ICON) return oldspacing;
-
+
/* set to defaults, if instructed to */
- if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
- if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
-
- /* if 0 then compute width
- * FIXME: Should scan each item and determine max width of
- * icon or label, then make that the width */
- if (cx == 0)
- cx = infoPtr->iconSpacing.cx;
-
- /* if 0 then compute height */
- if (cy == 0)
- cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
- ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
-
+ if (cx == -1 && cy == -1)
+ {
+ infoPtr->autoSpacing = TRUE;
+ if (infoPtr->himlNormal)
+ ImageList_GetIconSize(infoPtr->himlNormal, &iconWidth, &iconHeight);
+ cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON) + iconWidth;
+ cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON) + iconHeight;
+ }
+ else
+ infoPtr->autoSpacing = FALSE;
+
+ /* if 0 then keep width */
+ if (cx != 0)
+ infoPtr->iconSpacing.cx = cx;
- infoPtr->iconSpacing.cx = cx;
- infoPtr->iconSpacing.cy = cy;
+ /* if 0 then keep height */
+ if (cy != 0)
+ infoPtr->iconSpacing.cy = cy;
TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
- LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
+ LOWORD(oldspacing), HIWORD(oldspacing), infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy,
infoPtr->iconSize.cx, infoPtr->iconSize.cy,
infoPtr->ntmHeight);
return oldspacing;
}
-static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
+static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL is_small)
{
INT cx, cy;
}
else
{
- size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
- size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
+ size->cx = GetSystemMetrics(is_small ? SM_CXSMICON : SM_CXICON);
+ size->cy = GetSystemMetrics(is_small ? SM_CYSMICON : SM_CYICON);
}
}
INT oldHeight = infoPtr->nItemHeight;
HIMAGELIST himlOld = 0;
- TRACE("(nType=%d, himl=%p\n", nType, himl);
+ TRACE("(nType=%d, himl=%p)\n", nType, himl);
switch (nType)
{
himlOld = infoPtr->himlNormal;
infoPtr->himlNormal = himl;
if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
- LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
+ if (infoPtr->autoSpacing)
+ LISTVIEW_SetIconSpacing(infoPtr, -1, -1);
break;
case LVSIL_SMALL:
himlOld = infoPtr->himlSmall;
infoPtr->himlSmall = himl;
if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
+ if (infoPtr->hwndHeader)
+ SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)himl);
break;
case LVSIL_STATE:
{
POINT Origin, Pt;
- TRACE("(nItem=%d, pt=%s\n", nItem, wine_dbgstr_point(pt));
+ TRACE("(nItem=%d, pt=%s)\n", nItem, wine_dbgstr_point(pt));
if (!pt || nItem < 0 || nItem >= infoPtr->nItemCount ||
!(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
* PARAMETER(S):
* [I] infoPtr : valid pointer to the listview structure
* [I] nItem : item index
- * [I] lpLVItem : item or subitem info
+ * [I] item : item or subitem info
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
-static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
+static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *item)
{
- BOOL bResult = TRUE;
+ BOOL ret = TRUE;
LVITEMW lvItem;
+ if (!item) return FALSE;
+
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
lvItem.mask = LVIF_STATE;
- lvItem.state = lpLVItem->state;
- lvItem.stateMask = lpLVItem->stateMask;
- TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
+ lvItem.state = item->state;
+ lvItem.stateMask = item->stateMask;
+ TRACE("item=%s\n", debuglvitem_t(&lvItem, TRUE));
if (nItem == -1)
{
+ UINT oldstate = 0;
+ BOOL notify;
+
+ /* special case optimization for recurring attempt to deselect all */
+ if (lvItem.state == 0 && lvItem.stateMask == LVIS_SELECTED && !LISTVIEW_GetSelectedCount(infoPtr))
+ return TRUE;
+
/* select all isn't allowed in LVS_SINGLESEL */
if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
return FALSE;
/* focus all isn't allowed */
if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
+ notify = infoPtr->bDoChangeNotify;
+ if (infoPtr->dwStyle & LVS_OWNERDATA)
+ {
+ infoPtr->bDoChangeNotify = FALSE;
+ if (!(lvItem.state & LVIS_SELECTED) && LISTVIEW_GetSelectedCount(infoPtr))
+ oldstate |= LVIS_SELECTED;
+ if (infoPtr->nFocusedItem != -1) oldstate |= LVIS_FOCUSED;
+ }
+
/* apply to all items */
for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
- if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
+ if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) ret = FALSE;
+
+ if (infoPtr->dwStyle & LVS_OWNERDATA)
+ {
+ NMLISTVIEW nmlv;
+
+ infoPtr->bDoChangeNotify = notify;
+
+ nmlv.iItem = -1;
+ nmlv.iSubItem = 0;
+ nmlv.uNewState = lvItem.state & lvItem.stateMask;
+ nmlv.uOldState = oldstate & lvItem.stateMask;
+ nmlv.uChanged = LVIF_STATE;
+ nmlv.ptAction.x = nmlv.ptAction.y = 0;
+ nmlv.lParam = 0;
+
+ notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
+ }
}
else
- bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
+ ret = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
- return bResult;
+ return ret;
}
/***
LVITEMW lvItem;
if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
+ if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
lvItem.iItem = nItem;
lvItem.iSubItem = lpLVItem->iSubItem;
*/
static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
{
- SIZE oldIconSize = infoPtr->iconSize;
HIMAGELIST himl;
if (infoPtr->uView == nView) return 1;
switch (nView)
{
case LV_VIEW_ICON:
- if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
- {
- TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
- oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
- LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
- }
- LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
- break;
case LV_VIEW_SMALLICON:
LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
break;
*/
static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
{
- HPEN hOldPen;
HDC hdc;
- INT oldROP;
if (infoPtr->xTrackLine == -1)
return FALSE;
if (!(hdc = GetDC(infoPtr->hwndSelf)))
return FALSE;
- hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
- oldROP = SetROP2(hdc, R2_XORPEN);
- MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
- LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
- SetROP2(hdc, oldROP);
- SelectObject(hdc, hOldPen);
+ PatBlt( hdc, infoPtr->xTrackLine, infoPtr->rcList.top,
+ 1, infoPtr->rcList.bottom - infoPtr->rcList.top, DSTINVERT );
ReleaseDC(infoPtr->hwndSelf, hdc);
return TRUE;
}
infoPtr->bRedraw = TRUE;
infoPtr->bNoItemMetrics = TRUE;
infoPtr->bDoChangeNotify = TRUE;
- infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
- infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
+ infoPtr->autoSpacing = TRUE;
+ infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON);
+ infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
infoPtr->nEditLabelItem = -1;
infoPtr->nLButtonDownItem = -1;
infoPtr->dwHoverTime = HOVER_DEFAULT; /* default system hover time */
+ infoPtr->cWheelRemainder = 0;
infoPtr->nMeasureItemHeight = 0;
infoPtr->xTrackLine = -1; /* no track line */
infoPtr->itemEdit.fEnabled = FALSE;
/* init item size to avoid division by 0 */
LISTVIEW_UpdateItemSize (infoPtr);
+ LISTVIEW_UpdateSize (infoPtr);
if (infoPtr->uView == LV_VIEW_DETAILS)
{
static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
{
- INT gcWheelDelta = 0;
- INT pulScrollLines = 3;
+ UINT pulScrollLines = 3;
TRACE("(wheelDelta=%d)\n", wheelDelta);
- SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
- gcWheelDelta -= wheelDelta;
-
switch(infoPtr->uView)
{
case LV_VIEW_ICON:
* listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
* should be fixed in the future.
*/
- LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
+ LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (wheelDelta > 0) ?
-LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE);
break;
case LV_VIEW_DETAILS:
- if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
+ SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
+
+ /* if scrolling changes direction, ignore left overs */
+ if ((wheelDelta < 0 && infoPtr->cWheelRemainder < 0) ||
+ (wheelDelta > 0 && infoPtr->cWheelRemainder > 0))
+ infoPtr->cWheelRemainder += wheelDelta;
+ else
+ infoPtr->cWheelRemainder = wheelDelta;
+ if (infoPtr->cWheelRemainder && pulScrollLines)
{
- int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
- cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
- LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll);
+ int cLineScroll;
+ pulScrollLines = min((UINT)LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
+ cLineScroll = pulScrollLines * (float)infoPtr->cWheelRemainder / WHEEL_DELTA;
+ infoPtr->cWheelRemainder -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines;
+ LISTVIEW_VScroll(infoPtr, SB_INTERNAL, -cLineScroll);
}
break;
case LV_VIEW_LIST:
- LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0);
+ LISTVIEW_HScroll(infoPtr, (wheelDelta > 0) ? SB_LINELEFT : SB_LINERIGHT, 0);
break;
}
return 0;
{
TRACE("()\n");
- /* if we did not have the focus, there's nothing to do */
+ /* drop any left over scroll amount */
+ infoPtr->cWheelRemainder = 0;
+
+ /* if we did not have the focus, there's nothing more to do */
if (!infoPtr->bFocus) return 0;
/* send NM_KILLFOCUS notification */
return 0;
}
+static LRESULT LISTVIEW_TrackMouse(const LISTVIEW_INFO *infoPtr, POINT pt)
+{
+ MSG msg;
+ RECT r;
+
+ r.top = r.bottom = pt.y;
+ r.left = r.right = pt.x;
+
+ InflateRect(&r, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
+
+ SetCapture(infoPtr->hwndSelf);
+
+ while (1)
+ {
+ if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
+ {
+ if (msg.message == WM_MOUSEMOVE)
+ {
+ pt.x = (short)LOWORD(msg.lParam);
+ pt.y = (short)HIWORD(msg.lParam);
+ if (PtInRect(&r, pt))
+ continue;
+ else
+ {
+ ReleaseCapture();
+ return 1;
+ }
+ }
+ else if (msg.message >= WM_LBUTTONDOWN &&
+ msg.message <= WM_RBUTTONDBLCLK)
+ {
+ break;
+ }
+
+ DispatchMessageW(&msg);
+ }
+
+ if (GetCapture() != infoPtr->hwndSelf)
+ return 0;
+ }
+
+ ReleaseCapture();
+ return 0;
+}
+
+
/***
* DESCRIPTION:
* Processes mouse down messages (left mouse button).
* RETURN:
* Zero
*/
-static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, const NMHDR *lpnmhdr)
+static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, NMHDR *lpnmhdr)
{
- const NMHEADERW *lpnmh;
+ NMHEADERW *lpnmh;
TRACE("(lpnmhdr=%p)\n", lpnmhdr);
/* remember: HDN_LAST < HDN_FIRST */
if (lpnmhdr->code > HDN_FIRST || lpnmhdr->code < HDN_LAST) return 0;
- lpnmh = (const NMHEADERW *)lpnmhdr;
+ lpnmh = (NMHEADERW *)lpnmhdr;
if (lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
infoPtr->xTrackLine = x + ptOrigin.x;
LISTVIEW_DrawTrackLine(infoPtr);
- break;
+ return notify_forward_header(infoPtr, lpnmh);
}
-
+
case HDN_ENDTRACKA:
case HDN_ENDTRACKW:
/* remove the track line (if any) */
LISTVIEW_DrawTrackLine(infoPtr);
infoPtr->xTrackLine = -1;
- break;
+ return notify_forward_header(infoPtr, lpnmh);
case HDN_BEGINDRAG:
- notify_forward_header(infoPtr, lpnmh);
- return (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0;
+ if ((infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0) return 1;
+ return notify_forward_header(infoPtr, lpnmh);
case HDN_ENDDRAG:
infoPtr->colRectsDirty = TRUE;
LISTVIEW_InvalidateList(infoPtr);
- notify_forward_header(infoPtr, lpnmh);
- return FALSE;
-
+ return notify_forward_header(infoPtr, lpnmh);
+
case HDN_ITEMCHANGEDW:
case HDN_ITEMCHANGEDA:
{
LISTVIEW_InvalidateRect(infoPtr, &rcCol);
}
}
- }
- break;
+ break;
+ }
case HDN_ITEMCLICKW:
case HDN_ITEMCLICKA:
nmlv.iItem = -1;
nmlv.iSubItem = lpnmh->iItem;
notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
- notify_forward_header(infoPtr, lpnmh);
+ return notify_forward_header(infoPtr, lpnmh);
}
- break;
case HDN_DIVIDERDBLCLICKW:
case HDN_DIVIDERDBLCLICKA:
we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
split needed for that */
LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
- notify_forward_header(infoPtr, lpnmh);
- break;
+ return notify_forward_header(infoPtr, lpnmh);
}
-
return 0;
}
/***
* DESCRIPTION:
- * Processes mouse down messages (right mouse button).
+ * Processes WM_RBUTTONDOWN message and corresponding drag operation.
*
* PARAMETER(S):
* [I] infoPtr : valid pointer to the listview structure
* [I] wKey : key flag
- * [I] x,y : mouse coordinate
+ * [I] x, y : mouse coordinate
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
{
- LVHITTESTINFO lvHitTestInfo;
- INT nItem;
+ LVHITTESTINFO ht;
+ INT item;
- TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y);
+ TRACE("(key=%hu, x=%d, y=%d)\n", wKey, x, y);
/* send NM_RELEASEDCAPTURE notification */
if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
+ /* determine the index of the selected item */
+ ht.pt.x = x;
+ ht.pt.y = y;
+ item = LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
+
/* make sure the listview control window has the focus */
if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
- /* set right button down flag */
- infoPtr->bRButtonDown = TRUE;
-
- /* determine the index of the selected item */
- lvHitTestInfo.pt.x = x;
- lvHitTestInfo.pt.y = y;
- nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
-
- if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
+ if ((item >= 0) && (item < infoPtr->nItemCount))
{
- LISTVIEW_SetItemFocus(infoPtr, nItem);
+ LISTVIEW_SetItemFocus(infoPtr, item);
if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
- !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
- LISTVIEW_SetSelection(infoPtr, nItem);
+ !LISTVIEW_GetItemState(infoPtr, item, LVIS_SELECTED))
+ LISTVIEW_SetSelection(infoPtr, item);
}
else
- {
LISTVIEW_DeselectAll(infoPtr);
- }
-
- return 0;
-}
-/***
- * DESCRIPTION:
- * Processes mouse up messages (right mouse button).
- *
- * PARAMETER(S):
- * [I] infoPtr : valid pointer to the listview structure
- * [I] wKey : key flag
- * [I] x,y : mouse coordinate
- *
- * RETURN:
- * Zero
- */
-static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
-{
- LVHITTESTINFO lvHitTestInfo;
- POINT pt;
-
- TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y);
+ if (LISTVIEW_TrackMouse(infoPtr, ht.pt))
+ {
+ if (ht.iItem != -1)
+ {
+ NMLISTVIEW nmlv;
- if (!infoPtr->bRButtonDown) return 0;
-
- /* set button flag */
- infoPtr->bRButtonDown = FALSE;
+ memset(&nmlv, 0, sizeof(nmlv));
+ nmlv.iItem = ht.iItem;
+ nmlv.ptAction = ht.pt;
- /* Send NM_RCLICK notification */
- lvHitTestInfo.pt.x = x;
- lvHitTestInfo.pt.y = y;
- LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
- if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
+ notify_listview(infoPtr, LVN_BEGINRDRAG, &nmlv);
+ }
+ }
+ else
+ {
+ SetFocus(infoPtr->hwndSelf);
- /* Change to screen coordinate for WM_CONTEXTMENU */
- pt = lvHitTestInfo.pt;
- ClientToScreen(infoPtr->hwndSelf, &pt);
+ ht.pt.x = x;
+ ht.pt.y = y;
+ LISTVIEW_HitTest(infoPtr, &ht, TRUE, FALSE);
- /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
- SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
- (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
+ if (notify_click(infoPtr, NM_RCLICK, &ht))
+ {
+ /* Send a WM_CONTEXTMENU message in response to the WM_RBUTTONUP */
+ SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
+ (WPARAM)infoPtr->hwndSelf, (LPARAM)GetMessagePos());
+ }
+ }
return 0;
}
-
/***
* DESCRIPTION:
* Sets the cursor.
static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
{
HFONT oldFont = infoPtr->hFont;
+ INT oldHeight = infoPtr->nItemHeight;
TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
LISTVIEW_SaveTextMetrics(infoPtr);
+ infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
+
if (infoPtr->uView == LV_VIEW_DETAILS)
{
SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
LISTVIEW_UpdateSize(infoPtr);
LISTVIEW_UpdateScroll(infoPtr);
}
+ else if (infoPtr->nItemHeight != oldHeight)
+ LISTVIEW_UpdateScroll(infoPtr);
if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
* The "2" is there to mimic the native control. I think it may be
* related to either padding or edges. (GLA 7/2002)
*/
- if (!(infoPtr->dwStyle & WS_HSCROLL))
+ if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
}
if (uNewView != uOldView)
{
- SIZE oldIconSize = infoPtr->iconSize;
HIMAGELIST himl;
SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
-
- if (uNewView == LVS_ICON)
- {
- if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
- {
- TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
- oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
- LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
- }
- }
- else if (uNewView == LVS_REPORT)
+
+ if (uNewView == LVS_REPORT)
{
HDLAYOUT hl;
WINDOWPOS wp;
return LISTVIEW_SetHoverTime(infoPtr, (DWORD)lParam);
case LVM_SETICONSPACING:
- return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
+ if(lParam == -1)
+ return LISTVIEW_SetIconSpacing(infoPtr, -1, -1);
+ return LISTVIEW_SetIconSpacing(infoPtr, LOWORD(lParam), HIWORD(lParam));
case LVM_SETIMAGELIST:
return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (POINT*)lParam);
case LVM_SETITEMSTATE:
- if (lParam == 0) return FALSE;
return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
case LVM_SETITEMTEXTA:
case WM_RBUTTONDOWN:
return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
- case WM_RBUTTONUP:
- return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
-
case WM_SETCURSOR:
return LISTVIEW_SetCursor(infoPtr, wParam, lParam);
case WM_SHOWWINDOW:
return LISTVIEW_ShowWindow(infoPtr, wParam, lParam);
- case WM_SIZE:
- return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
-
case WM_STYLECHANGED:
return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
case WM_WINDOWPOSCHANGED:
if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
{
- SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
- SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
-
- if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
- {
- if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
- }
+ SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
+ SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
- LISTVIEW_UpdateSize(infoPtr);
- LISTVIEW_UpdateScroll(infoPtr);
+ if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
+ {
+ if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
+ }
+ LISTVIEW_Size(infoPtr, ((WINDOWPOS *)lParam)->cx, ((WINDOWPOS *)lParam)->cy);
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);