svx/source/form/navigatortree.cxx | 1 vcl/unx/gtk3/gtkinst.cxx | 97 +++++++++++++++++++++----------------- 2 files changed, 56 insertions(+), 42 deletions(-)
New commits: commit ee541c2d173be7cb3bdc4c1594264609db316755 Author: Michael Weghorn <[email protected]> AuthorDate: Fri Jan 23 12:38:32 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Fri Jan 23 22:04:39 2026 +0100 tdf#130857 svx: Explicitly set first form navigator col to editable The first column needs to be editable in order to allow editing the name for any entry other than the root "Forms" entry. Therefore, call weld::TreeView::set_column_editables accordingly. The vcl/SalInstanceTreeView implementation currently always sets the first column to editable in its ctor: // by default, 1st one is editable, others not; override with set_column_editables m_xTreeView->SetTabEditable(0, true); The Qt implementation doesn't do that however, and the idea is to add any missing calls to weld::TreeView::set_column_editables, see the commit message in commit e276093f3ab2a94d9be30c3fd2d2e402f4a820c8 Author: Michael Weghorn <[email protected]> Date: Wed Jan 14 11:10:23 2026 +0100 tdf#130857 qt weld: Make TreeView columns readonly by default for more details. To trigger the relevant logic: * start Writer * "Form" -> "Form Navigator" * right-click on the "Forms" entry and select "New" -> "Form" * right-click on the newly added "Form" entry and select the "Rename" menu entry Change-Id: I3303e6c61f89e0f96061216fb1d3410c7534f69c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197945 Reviewed-by: Michael Weghorn <[email protected]> Tested-by: Jenkins diff --git a/svx/source/form/navigatortree.cxx b/svx/source/form/navigatortree.cxx index 4583e1ffa190..383abe53e9de 100644 --- a/svx/source/form/navigatortree.cxx +++ b/svx/source/form/navigatortree.cxx @@ -149,6 +149,7 @@ namespace svxform m_xTreeView->set_size_request(200, 200); m_xTreeView->set_selection_mode(SelectionMode::Multiple); + m_xTreeView->set_column_editables({ true }); m_pNavModel.reset(new NavigatorTreeModel()); Clear(); commit 535027dee61179b626fe96082583615321052929 Author: Michael Weghorn <[email protected]> AuthorDate: Fri Jan 23 11:59:20 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Fri Jan 23 22:04:29 2026 +0100 gtk weld: Don't use memcmp for GtkTreeIter equality check Due to padding, memcmp doesn't necessarily return 0 even if all members of both GtkTreeIter structs are the same. Quoting from [1]: > This function reads object representations, not the object values, and > is typically meaningful for only trivially-copyable objects that have no > padding. Therefore, stop using memcp in GtkTreeIter::equal and instead compare the tree paths of the iterators. This requires having access to the GtkTreeModel that the GtkTreeIter objects belong to, so also store the model in GtkInstanceTreeIter. Move the existing logic from GtkInstanceTreeView::iter_compare to a new method GtkInstanceTreeIter::compare that can be used from GtkInstanceTreeView::iter_compare and for the new GtkInstanceTreeIter::equal implementation. Drop remaining GtkInstanceTreeIter logic directly writing to specific memory locations because it's no longer relevant now. This fixes a crash seen with the gtk3 VCL plugin for the following scenario after commit 3218a3c1c6e8639f9d8d1768e9dac398a00630a7 Author: Michael Weghorn <[email protected]> Date: Tue Jan 20 19:52:12 2026 +0100 tdf#130857 Simplify various weld::TreeIter equality checks which resulted in the preexisting bug to be triggered in that scenario now. This is because weld::TreeIter::equal gets used instead of weld::TreeView::iter_compare now and that would incorrectly report false in the check whether the TreeIter is the root entry in NavigatorTree::SynchronizeMarkList (frame 5 in below backtrace). Steps to reproduce: * start Writer with the gtk3 VCL plugin * "Form" -> "Form Navigator" * double-click on the "Forms" entry Backtrace of the crash: Thread 1 received signal SIGSEGV, Segmentation fault. 0x00007fe33e53bc55 in std::__uniq_ptr_impl<FmEntryDataList, std::default_delete<FmEntryDataList> >::_M_ptr (this=0x30) at /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/bits/unique_ptr.h:192 192 pointer _M_ptr() const noexcept { return std::get<0>(_M_t); } (rr) bt #0 0x00007fe33e53bc55 in std::__uniq_ptr_impl<FmEntryDataList, std::default_delete<FmEntryDataList> >::_M_ptr (this=0x30) at /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/bits/unique_ptr.h:192 #1 0x00007fe33e53bc35 in std::unique_ptr<FmEntryDataList, std::default_delete<FmEntryDataList> >::get (this=0x30) at /usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/bits/unique_ptr.h:472 #2 0x00007fe33e6b1089 in FmEntryData::GetChildList (this=0x0) at svx/source/inc/fmexpl.hxx:156 #3 0x00007fe33e6adcef in svxform::NavigatorTree::CollectObjects (this=0x561f5d1c7000, pFormData=0x0, bDeep=false, _rObjects=std::__debug::set with 0 elements) at /home/michi/development/git/libreoffice/svx/source/form/navigatortree.cxx:1970 #4 0x00007fe33e6acf85 in svxform::NavigatorTree::MarkViewObj (this=0x561f5d1c7000, pFormData=0x0, bDeep=false) at /home/michi/development/git/libreoffice/svx/source/form/navigatortree.cxx:1931 #5 0x00007fe33e6ac3d9 in svxform::NavigatorTree::SynchronizeMarkList (this=0x561f5d1c7000) at /home/michi/development/git/libreoffice/svx/source/form/navigatortree.cxx:1851 #6 0x00007fe33e6ac1b9 in svxform::NavigatorTree::OnSynchronizeTimer (this=0x561f5d1c7000) at /home/michi/development/git/libreoffice/svx/source/form/navigatortree.cxx:1438 #7 0x00007fe33e6a0c8d in svxform::NavigatorTree::LinkStubOnSynchronizeTimer (instance=0x561f5d1c7000, data=0x561f5d1c7110) at /home/michi/development/git/libreoffice/svx/source/form/navigatortree.cxx:1436 #8 0x00007fe33b52ecb1 in Link<Timer*, void>::Call (this=0x561f5d1c7130, data=0x561f5d1c7110) at include/tools/link.hxx:105 #9 0x00007fe33b52eb1c in Timer::Invoke (this=0x561f5d1c7110) at /home/michi/development/git/libreoffice/vcl/source/app/timer.cxx:75 #10 0x00007fe33b4d696d in Scheduler::CallbackTaskScheduling () at /home/michi/development/git/libreoffice/vcl/source/app/scheduler.cxx:615 #11 0x00007fe332a7da52 in SalTimer::CallCallback (this=0x561f567ddf80) at vcl/inc/saltimer.hxx:53 #12 0x00007fe332a7cb12 in sal_gtk_timeout_dispatch (pSource=0x561f5d3bcc30) at /home/michi/development/git/libreoffice/vcl/unx/gtk3/gtkdata.cxx:734 #13 0x00007fe3363016ae in g_main_dispatch (context=context@entry=0x561f556368d0) at ../../../glib/gmain.c:3565 #14 0x00007fe336304a4f in g_main_context_dispatch_unlocked (context=0x561f556368d0) at ../../../glib/gmain.c:4425 #15 g_main_context_iterate_unlocked (context=context@entry=0x561f556368d0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../../../glib/gmain.c:4490 #16 0x00007fe3363051d0 in g_main_context_iteration (context=0x561f556368d0, may_block=1) at ../../../glib/gmain.c:4556 #17 0x00007fe332a7b09c in GtkSalData::Yield (this=0x561f554ea910, bWait=true, bHandleAllCurrentEvents=false) at /home/michi/development/git/libreoffice/vcl/unx/gtk3/gtkdata.cxx:403 #18 0x00007fe332a800f3 in GtkInstance::DoYield (this=0x561f554ea790, bWait=true, bHandleAllCurrentEvents=false) at /home/michi/development/git/libreoffice/vcl/unx/gtk3/gtkinst.cxx:452 #19 0x00007fe33b502166 in InnerYield (i_bWait=true, i_bAllEvents=false) at /home/michi/development/git/libreoffice/vcl/source/app/svapp.cxx:389 #20 0x00007fe33b5019ef in Application::Yield () at /home/michi/development/git/libreoffice/vcl/source/app/svapp.cxx:502 #21 0x00007fe33b5017d0 in Application::Execute () at /home/michi/development/git/libreoffice/vcl/source/app/svapp.cxx:364 #22 0x00007fe344b228d5 in desktop::Desktop::Main (this=0x7ffe70f850d0) at /home/michi/development/git/libreoffice/desktop/source/app/app.cxx:1681 #23 0x00007fe33b52b1d6 in ImplSVMain () at /home/michi/development/git/libreoffice/vcl/source/app/svmain.cxx:230 #24 0x00007fe33b52ccf9 in SVMain () at /home/michi/development/git/libreoffice/vcl/source/app/svmain.cxx:248 #25 0x00007fe344b9c88a in soffice_main () at /home/michi/development/git/libreoffice/desktop/source/app/sofficemain.cxx:122 #26 0x0000561f264929fd in sal_main () at /home/michi/development/git/libreoffice/desktop/source/app/main.c:51 #27 0x0000561f264929d7 in main (argc=3, argv=0x7ffe70f852d8) at /home/michi/development/git/libreoffice/desktop/source/app/main.c:49 [1] https://en.cppreference.com/w/cpp/string/byte/memcmp Change-Id: I8600ec265f248266d996c2b39476203272987736 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197940 Tested-by: Jenkins Reviewed-by: Michael Weghorn <[email protected]> diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx index 5b8afb7004f5..e8eb6fc8c7a7 100644 --- a/vcl/unx/gtk3/gtkinst.cxx +++ b/vcl/unx/gtk3/gtkinst.cxx @@ -13657,21 +13657,40 @@ namespace struct GtkInstanceTreeIter : public weld::TreeIter { - GtkInstanceTreeIter(const GtkInstanceTreeIter* pOrig) + GtkInstanceTreeIter(GtkTreeModel* pTreeModel, const GtkTreeIter* pOrig) + : m_pTreeModel(pTreeModel) { if (pOrig) - iter = pOrig->iter; - else - memset(&iter, 0, sizeof(iter)); + iter = *pOrig; } - GtkInstanceTreeIter(const GtkTreeIter& rOrig) + + GtkInstanceTreeIter(GtkTreeModel* pTreeModel, const GtkTreeIter& rOrig) + : GtkInstanceTreeIter(pTreeModel, &rOrig) { - memcpy(&iter, &rOrig, sizeof(iter)); } + virtual bool equal(const TreeIter& rOther) const override { - return memcmp(&iter, &static_cast<const GtkInstanceTreeIter&>(rOther).iter, sizeof(GtkTreeIter)) == 0; + return compare(rOther) == 0; } + + int compare(const TreeIter& rOther) const + { + const GtkInstanceTreeIter& rOtherGtkIter = static_cast<const GtkInstanceTreeIter&>(rOther); + assert(m_pTreeModel == rOtherGtkIter.m_pTreeModel && "Comparing iters from different tree models"); + + GtkTreePath* pOwnPath = gtk_tree_model_get_path(m_pTreeModel, const_cast<GtkTreeIter*>(&iter)); + GtkTreePath* pOtherPath = gtk_tree_model_get_path(rOtherGtkIter.m_pTreeModel, const_cast<GtkTreeIter*>(&rOtherGtkIter.iter)); + + int nRet = gtk_tree_path_compare(pOwnPath, pOtherPath); + + gtk_tree_path_free(pOwnPath); + gtk_tree_path_free(pOtherPath); + + return nRet; + } + + GtkTreeModel* m_pTreeModel; GtkTreeIter iter; }; @@ -13894,8 +13913,8 @@ public: virtual std::unique_ptr<weld::TreeIter> make_iterator(const weld::TreeIter* pOrig) const override { - return std::unique_ptr<weld::TreeIter>( - new GtkInstanceTreeIter(static_cast<const GtkInstanceTreeIter*>(pOrig))); + const GtkTreeIter* pGtkIter = pOrig ? &static_cast<const GtkInstanceTreeIter*>(pOrig)->iter : nullptr; + return std::make_unique<GtkInstanceTreeIter>(m_pTreeModel, pGtkIter); } virtual bool get_iter_first(weld::TreeIter& rIter) const override @@ -13905,6 +13924,7 @@ public: if (!gtk_tree_model_get_iter_first(m_pTreeModel, &aFirstIter)) return false; + rGtkIter.m_pTreeModel = m_pTreeModel; rGtkIter.iter = aFirstIter; return true; } @@ -13939,7 +13959,7 @@ public: { GtkTreeIter iter; if (gtk_tree_model_iter_nth_child(m_pTreeModel, &iter, nullptr, nPos)) - return std::make_unique<GtkInstanceTreeIter>(iter); + return std::make_unique<GtkInstanceTreeIter>(m_pTreeModel, iter); return {}; } @@ -14232,7 +14252,7 @@ private: // if there's a preexisting placeholder child, required to make this // potentially expandable in the first place, now we remove it - GtkInstanceTreeIter aIter(iter); + GtkInstanceTreeIter aIter(m_pTreeModel, iter); GtkTreePath* pPlaceHolderPath = nullptr; bool bPlaceHolder = child_is_placeholder(aIter); if (bPlaceHolder) @@ -14267,7 +14287,7 @@ private: { disable_notify_events(); - GtkInstanceTreeIter aIter(iter); + GtkInstanceTreeIter aIter(m_pTreeModel, iter); bool bRet = signal_collapsing(aIter); enable_notify_events(); @@ -14298,7 +14318,7 @@ private: set(iter, m_aToggleTriStateMap[nCol], false); - signal_toggled(iter_col(GtkInstanceTreeIter(iter), to_external_model(nCol))); + signal_toggled(iter_col(GtkInstanceTreeIter(m_pTreeModel, iter), to_external_model(nCol))); gtk_tree_path_free(tree_path); } @@ -14316,7 +14336,7 @@ private: { GtkTreePath *tree_path = gtk_tree_path_new_from_string(path); - GtkInstanceTreeIter aGtkIter(nullptr); + GtkInstanceTreeIter aGtkIter(m_pTreeModel, nullptr); gtk_tree_model_get_iter(m_pTreeModel, &aGtkIter.iter, tree_path); gtk_tree_path_free(tree_path); @@ -14342,7 +14362,7 @@ private: { GtkTreePath *tree_path = gtk_tree_path_new_from_string(path); - GtkInstanceTreeIter aGtkIter(nullptr); + GtkInstanceTreeIter aGtkIter(m_pTreeModel, nullptr); gtk_tree_model_get_iter(m_pTreeModel, &aGtkIter.iter, tree_path); gtk_tree_path_free(tree_path); @@ -14453,7 +14473,7 @@ private: gint sort_func(GtkTreeModel* pModel, GtkTreeIter* a, GtkTreeIter* b) { if (m_aCustomSort) - return m_aCustomSort(GtkInstanceTreeIter(*a), GtkInstanceTreeIter(*b)); + return m_aCustomSort(GtkInstanceTreeIter(m_pTreeModel, *a), GtkInstanceTreeIter(m_pTreeModel, *b)); return default_sort_func(pModel, a, b, m_xSorter.get()); } @@ -14522,7 +14542,7 @@ private: #endif SolarMutexGuard g; - OUString aTooltip = pThis->signal_query_tooltip(GtkInstanceTreeIter(iter)); + OUString aTooltip = pThis->signal_query_tooltip(GtkInstanceTreeIter(pModel, iter)); if (!aTooltip.isEmpty()) { gtk_tooltip_set_text(tooltip, OUStringToOString(aTooltip, RTL_TEXTENCODING_UTF8).getStr()); @@ -14985,6 +15005,7 @@ public: if (pRet) { GtkInstanceTreeIter* pGtkRetIter = static_cast<GtkInstanceTreeIter*>(pRet); + pGtkRetIter->m_pTreeModel = m_pTreeModel; pGtkRetIter->iter = iter; } enable_notify_events(); @@ -15042,7 +15063,7 @@ public: pGtkIter->iter = restore; } - GtkInstanceTreeIter aGtkIter(nullptr); + GtkInstanceTreeIter aGtkIter(m_pTreeModel, nullptr); if (pFixedWidths) set_column_fixed_widths(*pFixedWidths); @@ -15242,7 +15263,7 @@ public: { g_object_freeze_notify(G_OBJECT(m_pTreeModel)); - GtkInstanceTreeIter aGtkIter(nullptr); + GtkInstanceTreeIter aGtkIter(m_pTreeModel, nullptr); if (get_iter_first(aGtkIter)) { do @@ -15259,7 +15280,7 @@ public: { g_object_freeze_notify(G_OBJECT(m_pTreeModel)); - GtkInstanceTreeIter aGtkIter(nullptr); + GtkInstanceTreeIter aGtkIter(m_pTreeModel, nullptr); GtkTreeModel* pModel; GList* pList = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(m_pTreeView), &pModel); @@ -15288,7 +15309,7 @@ public: return; } - GtkInstanceTreeIter aGtkIter(nullptr); + GtkInstanceTreeIter aGtkIter(m_pTreeModel, nullptr); gtk_tree_model_get_iter(m_pTreeModel, &aGtkIter.iter, start_path); do @@ -15464,17 +15485,7 @@ public: virtual int iter_compare(const weld::TreeIter& a, const weld::TreeIter& b) const override { const GtkInstanceTreeIter& rGtkIterA = static_cast<const GtkInstanceTreeIter&>(a); - const GtkInstanceTreeIter& rGtkIterB = static_cast<const GtkInstanceTreeIter&>(b); - - GtkTreePath* pathA = gtk_tree_model_get_path(m_pTreeModel, const_cast<GtkTreeIter*>(&rGtkIterA.iter)); - GtkTreePath* pathB = gtk_tree_model_get_path(m_pTreeModel, const_cast<GtkTreeIter*>(&rGtkIterB.iter)); - - int nRet = gtk_tree_path_compare(pathA, pathB); - - gtk_tree_path_free(pathB); - gtk_tree_path_free(pathA); - - return nRet; + return rGtkIterA.compare(b); } // by copy and delete of old copy @@ -15561,6 +15572,7 @@ public: { const GtkInstanceTreeIter& rGtkSource(static_cast<const GtkInstanceTreeIter&>(rSource)); GtkInstanceTreeIter& rGtkDest(static_cast<GtkInstanceTreeIter&>(rDest)); + rGtkDest.m_pTreeModel = rGtkSource.m_pTreeModel; rGtkDest.iter = rGtkSource.iter; } @@ -15568,7 +15580,7 @@ public: { GtkTreeIter iter; if (get_selected_iterator(&iter)) - return std::make_unique<GtkInstanceTreeIter>(iter); + return std::make_unique<GtkInstanceTreeIter>(m_pTreeModel, iter); return {}; } @@ -15583,7 +15595,7 @@ public: if (!path) return {}; gtk_tree_path_free(path); - return std::make_unique<GtkInstanceTreeIter>(iter); + return std::make_unique<GtkInstanceTreeIter>(m_pTreeModel, iter); } virtual void do_set_cursor(const weld::TreeIter& rIter) override @@ -15717,7 +15729,7 @@ public: virtual bool get_children_on_demand(const weld::TreeIter& rIter) const override { const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); - GtkInstanceTreeIter aIter(&rGtkIter); + GtkInstanceTreeIter aIter(m_pTreeModel, rGtkIter.iter); return child_is_placeholder(aIter); } @@ -15727,7 +15739,7 @@ public: disable_notify_events(); const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter); - GtkInstanceTreeIter aPlaceHolderIter(&rGtkIter); + GtkInstanceTreeIter aPlaceHolderIter(m_pTreeModel, rGtkIter.iter); bool bPlaceHolder = child_is_placeholder(aPlaceHolderIter); @@ -16013,7 +16025,7 @@ public: { GtkTreeIter iter; gtk_tree_model_get_iter(m_pTreeModel, &iter, path); - pResult = std::make_unique<GtkInstanceTreeIter>(iter); + pResult = std::make_unique<GtkInstanceTreeIter>(m_pTreeModel, iter); } if (m_bInDrag && bDnDMode) @@ -16453,7 +16465,7 @@ private: if (!gtk_icon_view_get_tooltip_context(pIconView, &x, &y, keyboard_tip, &pModel, &pPath, &iter)) return false; #endif - OUString aTooltip = pThis->signal_query_tooltip(GtkInstanceTreeIter(iter)); + OUString aTooltip = pThis->signal_query_tooltip(GtkInstanceTreeIter(pModel, iter)); if (!aTooltip.isEmpty()) { gtk_tooltip_set_text(tooltip, OUStringToOString(aTooltip, RTL_TEXTENCODING_UTF8).getStr()); @@ -16681,6 +16693,7 @@ public: if (pRet) { GtkInstanceTreeIter* pGtkRetIter = static_cast<GtkInstanceTreeIter*>(pRet); + pGtkRetIter->m_pTreeModel = m_pTreeModel; pGtkRetIter->iter = iter; } enable_notify_events(); @@ -16833,7 +16846,7 @@ public: { GtkTreeIter iter; if (get_selected_iterator(&iter)) - return std::make_unique<GtkInstanceTreeIter>(iter); + return std::make_unique<GtkInstanceTreeIter>(GTK_TREE_MODEL(m_pTreeStore), iter); return {}; } @@ -16843,13 +16856,13 @@ public: GtkTreeIter iter; GtkTreePath* path; gtk_icon_view_get_cursor(m_pIconView, &path, nullptr); + GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); if (path) { - GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); gtk_tree_model_get_iter(pModel, &iter, path); } if (path) - return std::make_unique<GtkInstanceTreeIter>(iter); + return std::make_unique<GtkInstanceTreeIter>(pModel, iter); return {}; } @@ -16879,7 +16892,7 @@ public: virtual void selected_foreach(const std::function<bool(weld::TreeIter&)>& func) override { - GtkInstanceTreeIter aGtkIter(nullptr); + GtkInstanceTreeIter aGtkIter(m_pTreeModel, nullptr); GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore); GList* pList = gtk_icon_view_get_selected_items(m_pIconView);
