Signed-off-by: Fabian Maurer dark.shadow4@web.de --- dlls/comctl32/listview.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-)
diff --git a/dlls/comctl32/listview.c b/dlls/comctl32/listview.c index 200bf93be5..fa0d18e5d6 100644 --- a/dlls/comctl32/listview.c +++ b/dlls/comctl32/listview.c @@ -211,6 +211,8 @@ typedef struct tagDELAYED_ITEM_EDIT INT iItem; } DELAYED_ITEM_EDIT;
+#define FLAG_NOTIFY_CHANGE (0x1) /* Whether we can send change notification messages */ + typedef struct tagLISTVIEW_INFO { /* control window */ @@ -225,7 +227,6 @@ typedef struct tagLISTVIEW_INFO /* notification window */ SHORT notifyFormat; HWND hwndNotify; - BOOL bDoChangeNotify; /* send change notification messages? */ UINT uCallbackMask;
/* tooltips */ @@ -326,6 +327,7 @@ typedef struct tagLISTVIEW_INFO
/* misc */ DWORD iVersion; /* CCM_[G,S]ETVERSION */ + DWORD notify_mask; } LISTVIEW_INFO;
/* @@ -3507,13 +3509,13 @@ Parameters: */ static void LISTVIEW_ShiftFocus(LISTVIEW_INFO *infoPtr, INT focus, INT item, INT direction) { - BOOL old_change = infoPtr->bDoChangeNotify; + BOOL old_change = infoPtr->notify_mask & FLAG_NOTIFY_CHANGE;
- infoPtr->bDoChangeNotify = FALSE; + infoPtr->notify_mask &= ~FLAG_NOTIFY_CHANGE; focus = shift_item(infoPtr, focus, item, direction); if (focus != infoPtr->nFocusedItem) LISTVIEW_SetItemFocus(infoPtr, focus); - infoPtr->bDoChangeNotify = old_change; + infoPtr->notify_mask |= old_change; }
/** @@ -3565,8 +3567,8 @@ static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem) * only one LVN_ODSTATECHANGED notification. * See MSDN documentation for LVN_ITEMCHANGED. */ - bOldChange = infoPtr->bDoChangeNotify; - if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE; + bOldChange = infoPtr->notify_mask & FLAG_NOTIFY_CHANGE; + if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->notify_mask &= ~FLAG_NOTIFY_CHANGE;
if (nFirst == -1) nFirst = nItem;
@@ -3585,7 +3587,7 @@ static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem) notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv); if (!IsWindow(hwndSelf)) return FALSE; - infoPtr->bDoChangeNotify = bOldChange; + infoPtr->notify_mask |= bOldChange; return TRUE; }
@@ -3656,8 +3658,8 @@ static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
/* disable per item notifications on LVS_OWNERDATA style FIXME: single LVN_ODSTATECHANGED should be used */ - bOldChange = infoPtr->bDoChangeNotify; - if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE; + bOldChange = infoPtr->notify_mask & FLAG_NOTIFY_CHANGE; + if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->notify_mask &= ~FLAG_NOTIFY_CHANGE;
LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
@@ -3668,7 +3670,7 @@ static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem) /* this will also destroy the selection */ iterator_destroy(&i);
- infoPtr->bDoChangeNotify = bOldChange; + infoPtr->notify_mask |= bOldChange;
LISTVIEW_SetItemFocus(infoPtr, nItem); } @@ -4257,7 +4259,7 @@ static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL 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) + if(lpItem && !isNew && (infoPtr->notify_mask & FLAG_NOTIFY_CHANGE)) { HWND hwndSelf = infoPtr->hwndSelf;
@@ -4350,7 +4352,7 @@ static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL
/* send LVN_ITEMCHANGED notification */ if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam; - if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv); + if (infoPtr->notify_mask & FLAG_NOTIFY_CHANGE) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
return TRUE; } @@ -8986,10 +8988,10 @@ static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITE /* focus all isn't allowed */ if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
- notify = infoPtr->bDoChangeNotify; + notify = infoPtr->notify_mask & FLAG_NOTIFY_CHANGE; if (infoPtr->dwStyle & LVS_OWNERDATA) { - infoPtr->bDoChangeNotify = FALSE; + infoPtr->notify_mask &= ~FLAG_NOTIFY_CHANGE; if (!(lvItem.state & LVIS_SELECTED) && LISTVIEW_GetSelectedCount(infoPtr)) oldstate |= LVIS_SELECTED; if (infoPtr->nFocusedItem != -1) oldstate |= LVIS_FOCUSED; @@ -9003,7 +9005,7 @@ static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITE { NMLISTVIEW nmlv;
- infoPtr->bDoChangeNotify = notify; + infoPtr->notify_mask |= notify;
nmlv.iItem = -1; nmlv.iSubItem = 0; @@ -9484,7 +9486,7 @@ static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, const CREATESTRUCTW * infoPtr->nHotItem = -1; infoPtr->redraw = TRUE; infoPtr->bNoItemMetrics = TRUE; - infoPtr->bDoChangeNotify = TRUE; + infoPtr->notify_mask = ~(0U); /* By default allow sending all notifications */ infoPtr->autoSpacing = TRUE; infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON); infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
Signed-off-by: Fabian Maurer dark.shadow4@web.de --- v2: -Use message tests -Check if were already processing LVN_ENDLABELEDIT on a different position (fixes new tests) --- dlls/comctl32/listview.c | 9 +++- dlls/comctl32/tests/listview.c | 76 ++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/dlls/comctl32/listview.c b/dlls/comctl32/listview.c index fa0d18e5d6..bb1aa52e90 100644 --- a/dlls/comctl32/listview.c +++ b/dlls/comctl32/listview.c @@ -212,6 +212,7 @@ typedef struct tagDELAYED_ITEM_EDIT } DELAYED_ITEM_EDIT;
#define FLAG_NOTIFY_CHANGE (0x1) /* Whether we can send change notification messages */ +#define FLAG_NOTIFY_ENDLABELEDIT (0x2) /* Whether we can send LVN_ENDLABELEDIT */
typedef struct tagLISTVIEW_INFO { @@ -5916,9 +5917,14 @@ static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL dispInfo.item.pszText = same ? NULL : pszText; dispInfo.item.cchTextMax = textlenT(dispInfo.item.pszText, isW);
+ /* Sending LVN_ENDLABELEDITW might trigger EN_KILLFOCUS which would call this function again */ + infoPtr->notify_mask &= ~FLAG_NOTIFY_ENDLABELEDIT; + /* Do we need to update the Item Text */ res = notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW);
+ infoPtr->notify_mask |= FLAG_NOTIFY_ENDLABELEDIT; + infoPtr->nEditLabelItem = -1; infoPtr->hwndEdit = 0;
@@ -11908,7 +11914,8 @@ static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lP } case EN_KILLFOCUS: { - LISTVIEW_CancelEditLabel(infoPtr); + if (infoPtr->notify_mask & FLAG_NOTIFY_ENDLABELEDIT) + LISTVIEW_CancelEditLabel(infoPtr); break; }
diff --git a/dlls/comctl32/tests/listview.c b/dlls/comctl32/tests/listview.c index c118f02026..ff46b472f3 100644 --- a/dlls/comctl32/tests/listview.c +++ b/dlls/comctl32/tests/listview.c @@ -75,6 +75,8 @@ static BOOL g_disp_A_to_W; static NMLVDISPINFOA g_editbox_disp_info; /* when this is set focus will be tested on LVN_DELETEITEM */ static BOOL g_focus_test_LVN_DELETEITEM; +/* Whether to send WM_KILLFOCUS to the edit control during LVN_ENDLABELEDIT */ +static BOOL do_LVN_ENDLABELEDIT_killfocus = FALSE;
static HWND subclass_editbox(HWND hwndListview);
@@ -445,6 +447,25 @@ static const struct message parent_list_cd_seq[] = { { 0 } };
+static const struct message listview_LVN_ENDLABELEDITA[] = { + { WM_NOTIFY, sent|id, 0, 0, LVN_ENDLABELEDITA }, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING}, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED }, + { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW }, /* XP */ + { WM_NOTIFY, sent|id, 0, 0, NM_SETFOCUS }, + { 0 } +}; + +static const struct message listview_kill_focus_during_LVN_ENDLABELEDITA[] = { + { WM_NOTIFY, sent|id, 0, 0, LVN_ENDLABELEDITA }, + { WM_COMMAND, sent|id|optional, 0, 0, EN_KILLFOCUS }, /* todo: not sent by wine yet */ + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING }, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED }, + { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW }, /* XP */ + { WM_NOTIFY, sent|id, 0, 0, NM_SETFOCUS }, + { 0 } +}; + static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; @@ -457,6 +478,7 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP msg.wParam = wParam; msg.lParam = lParam; if (message == WM_NOTIFY && lParam) msg.id = ((NMHDR*)lParam)->code; + if (message == WM_COMMAND) msg.id = HIWORD(wParam);
/* log system messages, except for painting */ if (message < WM_USER && @@ -510,6 +532,9 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP ok(IsWindow(edit), "expected valid edit control handle\n"); ok((GetWindowLongA(edit, GWL_STYLE) & ES_MULTILINE) == 0, "edit is multiline\n");
+ if (do_LVN_ENDLABELEDIT_killfocus) + SendMessageA(edit, WM_KILLFOCUS, 0, 0); + return TRUE; } case LVN_BEGINSCROLL: @@ -6322,6 +6347,56 @@ static void test_LVSCW_AUTOSIZE(void) DestroyWindow(hwnd); }
+static void test_LVN_ENDLABELEDITW(void) +{ + HWND hwnd, hwndedit; + LVITEMW item = {0}; + WCHAR text[] = {'l','a','l','a',0}; + DWORD ret; + + hwnd = create_listview_control(LVS_REPORT | LVS_EDITLABELS); + + insert_column(hwnd, 0); + + item.mask = LVIF_TEXT; + item.pszText = text; + ListView_InsertItemW(hwnd, &item); + + /* Test normal editing */ + SetFocus(hwnd); + hwndedit = (HWND)SendMessageW(hwnd, LVM_EDITLABELW, 0, 0); + + ret = SendMessageA(hwndedit, WM_SETTEXT, 0, (LPARAM)"test"); + expect(TRUE, ret); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(hwndedit, WM_KEYDOWN, VK_RETURN, 0); + ok_sequence(sequences, PARENT_SEQ_INDEX, listview_LVN_ENDLABELEDITA, + "kill focus during LVN_ENDLABELEDITA", FALSE); + + /* Test editing with kill focus */ + SetFocus(hwnd); + hwndedit = (HWND)SendMessageW(hwnd, LVM_EDITLABELW, 0, 0); + + ret = SendMessageA(hwndedit, WM_SETTEXT, 0, (LPARAM)"test2"); + expect(TRUE, ret); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + do_LVN_ENDLABELEDIT_killfocus = TRUE; + ret = SendMessageA(hwndedit, WM_KEYDOWN, VK_RETURN, 0); + do_LVN_ENDLABELEDIT_killfocus = FALSE; + + ok_sequence(sequences, PARENT_SEQ_INDEX, listview_kill_focus_during_LVN_ENDLABELEDITA, + "kill focus during LVN_ENDLABELEDITA", FALSE); + ok(GetFocus() == hwnd, "Expected combobox to be focused\n"); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + DestroyWindow(hwnd); +} + START_TEST(listview) { ULONG_PTR ctx_cookie; @@ -6425,6 +6500,7 @@ START_TEST(listview) test_oneclickactivate(); test_state_image(); test_LVSCW_AUTOSIZE(); + test_LVN_ENDLABELEDITW();
unload_v6_module(ctx_cookie, hCtx);