When the listview expands or collapses an item it notifies the parent using the TVN_ITEMEXPANDING message. The parent can return true on this message and it prevents the treeview from expanding or collapsing the item. WINE does not let you deny TVN_ITEMEXPANDING by returning true.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53727
-- v12: comctl32/treeview: Allow treeview parent to deny treeview expansion.
From: Jacob Czekalla jczekalla@codeweavers.com
--- dlls/comctl32/tests/treeview.c | 60 +++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-)
diff --git a/dlls/comctl32/tests/treeview.c b/dlls/comctl32/tests/treeview.c index db4cc4fda71..b9a59385371 100644 --- a/dlls/comctl32/tests/treeview.c +++ b/dlls/comctl32/tests/treeview.c @@ -51,6 +51,7 @@ static const char *g_endedit_overwrite_contents; static char *g_endedit_overwrite_ptr; static HFONT g_customdraw_font; static BOOL g_v6; +static int g_reject_tvn_itemexpanding = 0;
#define NUM_MSG_SEQUENCES 3 #define TREEVIEW_SEQ_INDEX 0 @@ -1361,7 +1362,11 @@ static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, } case TVN_ITEMEXPANDINGA: { - UINT newmask = pTreeView->itemNew.mask & ~TVIF_CHILDREN; + UINT newmask; + + if (g_reject_tvn_itemexpanding) + return TRUE; + newmask = pTreeView->itemNew.mask & ~TVIF_CHILDREN; ok(newmask == (TVIF_HANDLE | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_PARAM | TVIF_STATE), "got wrong mask %x\n", pTreeView->itemNew.mask); @@ -1837,6 +1842,7 @@ static void test_expandnotify(void) HWND hTree; BOOL ret; TVITEMA item; + RECT rc;
hTree = create_treeview_control(0); fill_tree(hTree); @@ -1898,6 +1904,58 @@ static void test_expandnotify(void)
DestroyWindow(hTree);
+ /* check that expansion can be denied by parent */ + hTree = create_treeview_control(0); + fill_tree(hTree); + + g_reject_tvn_itemexpanding = 1; + memset(&item, 0, sizeof(item)); + item.mask = TVIF_STATE; + item.hItem = hRoot; + item.state = TVIS_EXPANDED; + + SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)item.hItem); + g_reject_tvn_itemexpanding = 0; + + /* check if it's expanded */ + ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + todo_wine ok(!(item.state & TVIS_EXPANDED), "expected no expansion\n"); + + DestroyWindow(hTree); + + hTree = create_treeview_control(0); + fill_tree(hTree); + + memset(&item, 0, sizeof(item)); + item.hItem = hRoot; + item.mask = TVIF_STATE; + item.state = TVIS_EXPANDED; + + SendMessageA(hTree, TVM_EXPAND, TVE_EXPAND, (LPARAM)item.hItem); + + ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + todo_wine ok(item.state & TVIS_EXPANDED, "expected expansion\n"); + + memset(&item, 0, sizeof(item)); + item.hItem = hRoot; + item.mask = TVIF_STATE; + item.state = TVIS_EXPANDED; + *((HTREEITEM *)&rc) = item.hItem; + g_reject_tvn_itemexpanding = 1; + + /* simulate double click to get around TVIS_EXPANDEDONCE */ + SendMessageA(hTree, TVM_GETITEMRECT, TRUE, (LPARAM)&rc); + SendMessageA(hTree, WM_LBUTTONDBLCLK, MK_LBUTTON, MAKELPARAM(rc.left + ((rc.right - rc.left) / 2), rc.top + ((rc.bottom - rc.top) / 2))); + + ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + todo_wine ok(item.state & TVIS_EXPANDED, "expected expansion\n"); + g_reject_tvn_itemexpanding = 0; + + DestroyWindow(hTree); + /* test TVM_GETITEMRECT inside TVN_ITEMEXPANDED notification */ hTree = create_treeview_control(0); fill_tree(hTree);
From: Jacob Czekalla jczekalla@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53727 --- dlls/comctl32/tests/treeview.c | 6 +++--- dlls/comctl32/treeview.c | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/dlls/comctl32/tests/treeview.c b/dlls/comctl32/tests/treeview.c index b9a59385371..99845ea4947 100644 --- a/dlls/comctl32/tests/treeview.c +++ b/dlls/comctl32/tests/treeview.c @@ -1920,7 +1920,7 @@ static void test_expandnotify(void) /* check if it's expanded */ ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); expect(TRUE, ret); - todo_wine ok(!(item.state & TVIS_EXPANDED), "expected no expansion\n"); + ok(!(item.state & TVIS_EXPANDED), "expected no expansion\n");
DestroyWindow(hTree);
@@ -1936,7 +1936,7 @@ static void test_expandnotify(void)
ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); expect(TRUE, ret); - todo_wine ok(item.state & TVIS_EXPANDED, "expected expansion\n"); + ok(item.state & TVIS_EXPANDED, "expected expansion\n");
memset(&item, 0, sizeof(item)); item.hItem = hRoot; @@ -1951,7 +1951,7 @@ static void test_expandnotify(void)
ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); expect(TRUE, ret); - todo_wine ok(item.state & TVIS_EXPANDED, "expected expansion\n"); + ok(item.state & TVIS_EXPANDED, "expected expansion\n"); g_reject_tvn_itemexpanding = 0;
DestroyWindow(hTree); diff --git a/dlls/comctl32/treeview.c b/dlls/comctl32/treeview.c index e2a4280f131..073f62c66a2 100644 --- a/dlls/comctl32/treeview.c +++ b/dlls/comctl32/treeview.c @@ -3348,10 +3348,10 @@ static BOOL TREEVIEW_SendExpanding(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, UINT action) { - return !TREEVIEW_SendTreeviewNotify(infoPtr, TVN_ITEMEXPANDINGW, action, - TVIF_HANDLE | TVIF_STATE | TVIF_PARAM - | TVIF_IMAGE | TVIF_SELECTEDIMAGE, - 0, item); + return TREEVIEW_SendTreeviewNotify(infoPtr, TVN_ITEMEXPANDINGW, action, + TVIF_HANDLE | TVIF_STATE | TVIF_PARAM + | TVIF_IMAGE | TVIF_SELECTEDIMAGE, + 0, item); }
static VOID @@ -3383,8 +3383,8 @@ TREEVIEW_Collapse(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, if (!TREEVIEW_HasChildren(infoPtr, item)) return FALSE;
- if (bUser) - TREEVIEW_SendExpanding(infoPtr, item, action); + if (bUser && TREEVIEW_SendExpanding(infoPtr, item, action)) + return TRUE;
if (item->firstChild == NULL) return FALSE; @@ -3513,11 +3513,11 @@ TREEVIEW_Expand(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, !(item->state & TVIS_EXPANDEDONCE)); if (sendsNotifications) { - if (!TREEVIEW_SendExpanding(infoPtr, item, TVE_EXPAND)) - { - TRACE(" TVN_ITEMEXPANDING returned TRUE, exiting...\n"); - return FALSE; - } + if (TREEVIEW_SendExpanding(infoPtr, item, TVE_EXPAND)) + { + TRACE(" TVN_ITEMEXPANDING returned TRUE, exiting...\n"); + return TRUE; + } } if (!item->firstChild) return FALSE;
On Thu Aug 15 13:30:19 2024 +0000, Zhiyi Zhang wrote:
Have you tried TVE_COLLAPSE | TVE_COLLAPSERESET? See https://learn.microsoft.com/en-us/windows/win32/controls/tree-view-control-i... regarding TVIS_EXPANDEDONCE
TVE_COLLAPSE | TVE_COLLAPSERESET doesn't seem to work either. I could try resetting the flag manually, although it does say it will result in unpredictable behavior.
On Thu Aug 15 14:09:14 2024 +0000, Huw Davies wrote:
Presumably the second commit should be removing the `todo_wine`s.
Yes, of course. Thank you.
TVE_COLLAPSE | TVE_COLLAPSERESET doesn't seem to work either.
That's weird. I tried sending TVM_EXPAND with TVE_COLLAPSE and TVE_EXPAND with control spy and they work as expected. Could you double-check? Does TVE_TOGGLE work? Also, could you try to use PostMessage(TVM_EXPAND, TVE_COLLAPSE) and then call flush_events()?
I could try resetting the flag manually, although it does say it will result in unpredictable behavior.
Let's not do that then.
On Thu Aug 15 15:24:13 2024 +0000, Zhiyi Zhang wrote:
TVE_COLLAPSE | TVE_COLLAPSERESET doesn't seem to work either.
That's weird. I tried sending TVM_EXPAND with TVE_COLLAPSE and TVE_EXPAND with control spy and they work as expected. Could you double-check? Does TVE_TOGGLE work? Also, could you try to use PostMessage(TVM_EXPAND, TVE_COLLAPSE) and then call flush_events()?
I could try resetting the flag manually, although it does say it will
result in unpredictable behavior. Let's not do that then.
They both work individually, but if I try to send TVM_EXPAND with TVE_COLLAPSE after I expand it also using TVM_EXPAND it doesn't work. I tried toggle and using PostMessage with flush_events, but it was no different.