include/vcl/weld.hxx                 |   28 +++++++++++++++++++++++-----
 vcl/inc/jsdialog/jsdialogbuilder.hxx |    6 +++---
 vcl/inc/qt5/QtInstanceComboBox.hxx   |    4 ++--
 vcl/inc/salvtables.hxx               |    4 ++--
 vcl/jsdialog/jsdialogbuilder.cxx     |   12 ++++++------
 vcl/qt5/QtInstanceComboBox.cxx       |    4 ++--
 vcl/unx/gtk3/gtkinst.cxx             |   12 ++++++------
 7 files changed, 44 insertions(+), 26 deletions(-)

New commits:
commit 85a046f2fab23f8c75bfb704b823e7d17507c0a5
Author:     Michael Weghorn <[email protected]>
AuthorDate: Sun Nov 23 00:17:59 2025 +0100
Commit:     Michael Weghorn <[email protected]>
CommitDate: Mon Nov 24 16:02:50 2025 +0100

    tdf#130857 weld: Block signals in ComboBox::set_active{,_id}
    
    As is the case in other weld methods, signals about
    another ComboBox entry getting selected are not supposed
    to be emitted when the change happens programatically
    and not by manual user interaction.
    
    Therefore, don't call weld::ComboBox::m_aChangeHdl
    when the active entry was changed programmatically,
    by blocking signals in weld::ComboBox::set_active
    and weld::ComboBox::set_active_id and not calling
    the handler in weld::ComboBox::signal_changed
    while signal handling is blocked.
    
    Have the blocking logic in the new base class
    implementations and introduce new
    weld::ComboBox::do_set_active and
    weld::ComboBox::do_set_active_id as new
    virtual methods to override in the subclasses,
    which get called from the base class implementation.
    
    This aligns the QtInstanceComboBox behavior with
    the gtk and vcl ones (see how the GtkInstanceComboBox
    implementations of the method were already explicitly
    blocking the signal before this commit).
    
    Similar earlier commit:
    
        commit 34dfea68f77bf54ff45e32bf0e5ce6a4088ba8f8
        Author: Michael Weghorn <[email protected]>
        Date:   Mon Nov 17 09:49:14 2025 +0100
    
            tdf#130857 weld: Block signals in SpinButton::set_value
    
    Without this commit in place, an assert with the below
    backtrace was triggered in a WIP branch for supporting
    the dialog that can be triggered as follows when
    starting LO with the qt6 VCL plugin and environment
    variable SAL_VCL_QT_USE_WELDED_WIDGETS=1 set.
    
    * "Insert" -> "Table of Contents and Index" -> "Citation"
    * press "Edit" button
    
    Backtrace:
    
        Thread 1 received signal SIGABRT, Aborted.
        __pthread_kill_implementation (threadid=<optimized out>, 
signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
        warning: 44     ./nptl/pthread_kill.c: No such file or directory
        (rr) bt
        #0  __pthread_kill_implementation (threadid=<optimized out>, 
signo=signo@entry=6, no_tid=no_tid@entry=0) at ./nptl/pthread_kill.c:44
        #1  0x00007f4549c9e9ff in __pthread_kill_internal (threadid=<optimized 
out>, signo=6) at ./nptl/pthread_kill.c:89
        #2  0x00007f4549c49cc2 in __GI_raise (sig=sig@entry=6) at 
../sysdeps/posix/raise.c:26
        #3  0x00007f4549c324ac in __GI_abort () at ./stdlib/abort.c:73
        #4  0x00007f4549c32420 in __assert_fail_base (fmt=<optimized out>, 
assertion=<optimized out>, file=<optimized out>, line=1935, function=<optimized 
out>) at ./assert/assert.c:118
        #5  0x00007f45068bbd33 in (anonymous 
namespace)::SwCreateAuthEntryDlg_Impl::TargetTypeHdl (this=0x7fff91eb7988, 
rBox=...) at 
/home/michi/development/git/libreoffice/sw/source/ui/index/swuiidxmrk.cxx:1935
        #6  0x00007f45068bb1fd in (anonymous 
namespace)::SwCreateAuthEntryDlg_Impl::LinkStubTargetTypeHdl 
(instance=0x7fff91eb7988, data=...) at 
/home/michi/development/git/libreoffice/sw/source/ui/index/swuiidxmrk.cxx:1933
        #7  0x00007f4537c89a81 in Link<weld::ComboBox&, void>::Call 
(this=0x55b1cabf6820, data=...) at include/tools/link.hxx:105
        #8  0x00007f4537c8968c in weld::ComboBox::signal_changed 
(this=0x55b1cabf67d8) at include/vcl/weld.hxx:821
        #9  0x00007f4537c7faed in QtInstanceComboBox::signalChanged 
(this=0x55b1cabf6650) at vcl/qt6/../qt5/QtInstanceComboBox.cxx:381
        #10 0x00007f4537c89ea1 in 
QtPrivate::FunctorCall<std::integer_sequence<unsigned long>, QtPrivate::List<>, 
void, void (QtInstanceComboBox::*)()>::call(void (QtInstanceComboBox::*)(), 
QtInstanceComboBox*, void**)::{lambda()#1}::operator()() const 
(this=0x7fff91eb6830) at 
/home/michi/development/git/qt5/qtbase/src/corelib/kernel/qobjectdefs_impl.h:127
        #11 0x00007f4537c89dd9 in 
QtPrivate::FunctorCallBase::call_internal<void, 
QtPrivate::FunctorCall<std::integer_sequence<unsigned long>, QtPrivate::List<>, 
void, void (QtInstanceComboBox::*)()>::call(void (QtInstanceComboBox::*)(), 
QtInstanceComboBox*, void**)::{lambda()#1}>(void**, 
QtPrivate::FunctorCall<std::integer_sequence<unsigned long>, QtPrivate::List<>, 
void, void (QtInstanceComboBox::*)()>::call(void (QtInstanceComboBox::*)(), 
QtInstanceComboBox*, void**)::{lambda()#1}&&) (args=0x7fff91eb6c10, fn=...) at 
/home/michi/development/git/qt5/qtbase/src/corelib/kernel/qobjectdefs_impl.h:65
        #12 0x00007f4537c89d0b in 
QtPrivate::FunctorCall<std::integer_sequence<unsigned long>, QtPrivate::List<>, 
void, void (QtInstanceComboBox::*)()>::call(void (QtInstanceComboBox::*)(), 
QtInstanceComboBox*, void**)
            (f=(void (QtInstanceComboBox::*)(class QtInstanceComboBox * const)) 
0x7f4537c7fab0 <QtInstanceComboBox::signalChanged()>, o=0x55b1cabf6650, 
arg=0x7fff91eb6c10)
            at 
/home/michi/development/git/qt5/qtbase/src/corelib/kernel/qobjectdefs_impl.h:126
        #13 0x00007f4537c89c8d in QtPrivate::FunctionPointer<void 
(QtInstanceComboBox::*)()>::call<QtPrivate::List<>, void>(void 
(QtInstanceComboBox::*)(), QtInstanceComboBox*, void**)
            (f=(void (QtInstanceComboBox::*)(class QtInstanceComboBox * const)) 
0x7f4537c7fab0 <QtInstanceComboBox::signalChanged()>, o=0x55b1cabf6650, 
arg=0x7fff91eb6c10)
            at 
/home/michi/development/git/qt5/qtbase/src/corelib/kernel/qobjectdefs_impl.h:174
        #14 0x00007f4537c89bb6 in QtPrivate::QCallableObject<void 
(QtInstanceComboBox::*)(), QtPrivate::List<>, void>::impl(int, 
QtPrivate::QSlotObjectBase*, QObject*, void**, bool*)
            (which=1, this_=0x55b1cc892eb0, r=0x55b1cabf6650, a=0x7fff91eb6c10, 
ret=0x0) at 
/home/michi/development/git/qt5/qtbase/src/corelib/kernel/qobjectdefs_impl.h:545
        #15 0x00007f4536ae93a2 in QtPrivate::QSlotObjectBase::call 
(this=0x55b1cc892eb0, r=0x55b1cabf6650, a=0x7fff91eb6c10) at 
qtbase/src/corelib/kernel/qobjectdefs_impl.h:461
        #16 0x00007f4536d15132 in doActivate<false> (sender=0x55b1cc886740, 
signal_index=13, argv=0x7fff91eb6c10) at 
/home/michi/development/git/qt5/qtbase/src/corelib/kernel/qobject.cpp:4356
        #17 0x00007f4536d0b7f3 in QMetaObject::activate (sender=0x55b1cc886740, 
m=0x7f4535779438 <QComboBox::staticMetaObject>, local_signal_index=6, 
argv=0x7fff91eb6c10)
            at 
/home/michi/development/git/qt5/qtbase/src/corelib/kernel/qobject.cpp:4416
        #18 0x00007f4534c845fb in QMetaObject::activate<void, QString> 
(sender=0x55b1cc886740, mo=0x7f4535779438 <QComboBox::staticMetaObject>, 
local_signal_index=6, ret=0x0, args=...) at 
qtbase/src/corelib/kernel/qobjectdefs.h:319
        #19 0x00007f4534e3ed11 in QComboBox::currentTextChanged 
(this=0x55b1cc886740, _t1=...) at 
qtbase/src/widgets/Widgets_autogen/include/moc_qcombobox.cpp:374
        #20 0x00007f4534e3c17d in QComboBoxPrivate::updateCurrentText 
(this=0x55b1cc886910, text=...) at 
/home/michi/development/git/qt5/qtbase/src/widgets/widgets/qcombobox.cpp:3204
        #21 0x00007f4534e3c45e in QComboBoxPrivate::emitCurrentIndexChanged 
(this=0x55b1cc886910, index=...) at 
/home/michi/development/git/qt5/qtbase/src/widgets/widgets/qcombobox.cpp:1689
        #22 0x00007f4534e3750b in QComboBoxPrivate::setCurrentIndex 
(this=0x55b1cc886910, mi=...) at 
/home/michi/development/git/qt5/qtbase/src/widgets/widgets/qcombobox.cpp:2474
        #23 0x00007f4534e3693a in QComboBox::setCurrentIndex 
(this=0x55b1cc886740, index=3) at 
/home/michi/development/git/qt5/qtbase/src/widgets/widgets/qcombobox.cpp:2421
        #24 0x00007f4537c85482 in 
QtInstanceComboBox::set_active(int)::$_0::operator()() const 
(this=0x7fff91eb7038) at vcl/qt6/../qt5/QtInstanceComboBox.cxx:124
        #25 0x00007f4537c85455 in std::__invoke_impl<void, 
QtInstanceComboBox::set_active(int)::$_0&>(std::__invoke_other, 
QtInstanceComboBox::set_active(int)::$_0&) (__f=...)
            at 
/usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/bits/invoke.h:63
        #26 0x00007f4537c85405 in std::__invoke_r<void, 
QtInstanceComboBox::set_active(int)::$_0&>(QtInstanceComboBox::set_active(int)::$_0&)
 (__fn=...) at 
/usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/bits/invoke.h:113
        #27 0x00007f4537c8531d in std::_Function_handler<void(), 
QtInstanceComboBox::set_active(int)::$_0>::_M_invoke (__functor=...) at 
/usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/bits/std_function.h:292
        #28 0x00007f4537c2ff6e in std::function<void()>::operator() 
(this=0x7fff91eb7038) at 
/usr/lib/gcc/x86_64-linux-gnu/15/../../../../include/c++/15/bits/std_function.h:593
        #29 0x00007f4537c270af in QtInstance::RunInMainThread 
(this=0x55b1c4b3eb40, func=...) at vcl/qt6/../qt5/QtInstance.cxx:206
        #30 0x00007f4537c80db9 in QtInstanceComboBox::set_active 
(this=0x55b1cabf6650, nPos=3) at vcl/qt6/../qt5/QtInstanceComboBox.cxx:124
        #31 0x00007f45068bb588 in (anonymous 
namespace)::SwCreateAuthEntryDlg_Impl::SetFields (this=0x7fff91eb7988, 
pFields=0x55b1cbfdd378, bNewEntry=true) at 
/home/michi/development/git/libreoffice/sw/source/ui/index/swuiidxmrk.cxx:1831
        #32 0x00007f45068b93c5 in (anonymous 
namespace)::SwCreateAuthEntryDlg_Impl::SwCreateAuthEntryDlg_Impl 
(this=0x7fff91eb7988, pParent=0x55b1cc41bfb8, pFields=0x55b1cbfdd378, rSh=..., 
bNewEntry=true, bCreate=false)
            at 
/home/michi/development/git/libreoffice/sw/source/ui/index/swuiidxmrk.cxx:1796
        #33 0x00007f45068b6c66 in SwAuthorMarkPane::CreateEntryHdl 
(this=0x55b1cbfdd250, rButton=...) at 
/home/michi/development/git/libreoffice/sw/source/ui/index/swuiidxmrk.cxx:1426
        #34 0x00007f45068b546d in SwAuthorMarkPane::LinkStubCreateEntryHdl 
(instance=0x55b1cbfdd250, data=...) at 
/home/michi/development/git/libreoffice/sw/source/ui/index/swuiidxmrk.cxx:1420
        #35 0x00007f4540881dd1 in Link<weld::Button&, void>::Call 
(this=0x55b1cc1c8988, data=...) at include/tools/link.hxx:105
        #36 0x00007f454085ca1c in weld::Button::signal_clicked 
(this=0x55b1cc1c8980) at include/vcl/weld.hxx:1825
        #37 0x00007f454082ed18 in SalInstanceButton::ClickHdl 
(this=0x55b1cc1c87e0, pButton=0x55b1cbc63240) at 
/home/michi/development/git/libreoffice/vcl/source/app/salvtables.cxx:2931
        [...]
    
    Change-Id: I0136940e9d68cea9b64e58f3e172dfde170aff0c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194373
    Reviewed-by: Michael Weghorn <[email protected]>
    Tested-by: Jenkins

diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx
index 850b7321ddbb..2741d7f31fd0 100644
--- a/include/vcl/weld.hxx
+++ b/include/vcl/weld.hxx
@@ -818,7 +818,12 @@ protected:
 
     friend class ::LOKTrigger;
 
-    void signal_changed() { m_aChangeHdl.Call(*this); }
+    void signal_changed()
+    {
+        if (notify_events_disabled())
+            return;
+        m_aChangeHdl.Call(*this);
+    }
 
     virtual void signal_popup_toggled() { m_aPopupToggledHdl.Call(*this); }
 
@@ -832,6 +837,9 @@ protected:
     Link<vcl::RenderContext&, Size> m_aGetSizeHdl;
     Size signal_custom_get_size(vcl::RenderContext& rDevice) { return 
m_aGetSizeHdl.Call(rDevice); }
 
+    virtual void do_set_active(int pos) = 0;
+    virtual void do_set_active_id(const OUString& rStr) = 0;
+
 public:
     virtual void insert(int pos, const OUString& rStr, const OUString* pId,
                         const OUString* pIconName, VirtualDevice* 
pImageSurface)
@@ -874,7 +882,12 @@ public:
 
     //by index, returns -1 if nothing is selected
     virtual int get_active() const = 0;
-    virtual void set_active(int pos) = 0;
+    void set_active(int pos)
+    {
+        disable_notify_events();
+        do_set_active(pos);
+        enable_notify_events();
+    }
     virtual void remove(int pos) = 0;
 
     //by text
@@ -886,7 +899,12 @@ public:
 
     //by id
     virtual OUString get_active_id() const = 0;
-    virtual void set_active_id(const OUString& rStr) = 0;
+    void set_active_id(const OUString& rStr)
+    {
+        disable_notify_events();
+        do_set_active_id(rStr);
+        enable_notify_events();
+    }
     virtual OUString get_id(int pos) const = 0;
     virtual void set_id(int row, const OUString& rId) = 0;
     virtual int find_id(const OUString& rId) const = 0;
@@ -2433,7 +2451,7 @@ public:
 
     //by index
     virtual int get_active() const override { return 
m_xTreeView->get_selected_index(); }
-    virtual void set_active(int pos) override
+    virtual void do_set_active(int pos) override
     {
         m_xTreeView->set_cursor(pos);
         m_xTreeView->select(pos);
@@ -2451,7 +2469,7 @@ public:
 
     //by id
     virtual OUString get_active_id() const override { return 
m_xTreeView->get_selected_id(); }
-    virtual void set_active_id(const OUString& rStr) override
+    virtual void do_set_active_id(const OUString& rStr) override
     {
         m_xTreeView->select_id(rStr);
         m_xEntry->set_text(m_xTreeView->get_selected_text());
diff --git a/vcl/inc/jsdialog/jsdialogbuilder.hxx 
b/vcl/inc/jsdialog/jsdialogbuilder.hxx
index 1ac8f35e6026..135676b0ac8f 100644
--- a/vcl/inc/jsdialog/jsdialogbuilder.hxx
+++ b/vcl/inc/jsdialog/jsdialogbuilder.hxx
@@ -557,7 +557,7 @@ public:
     virtual void insert(int pos, const OUString& rStr, const OUString* pId,
                         const OUString* pIconName, VirtualDevice* 
pImageSurface) override;
     virtual void remove(int pos) override;
-    virtual void set_active(int pos) override;
+    virtual void do_set_active(int pos) override;
 };
 
 class JSComboBox final : public JSWidget<SalInstanceComboBoxWithEdit, 
::ComboBox>,
@@ -571,8 +571,8 @@ public:
     virtual void remove(int pos) override;
     void set_entry_text_without_notify(const OUString& rText);
     virtual void set_entry_text(const OUString& rText) override;
-    virtual void set_active(int pos) override;
-    virtual void set_active_id(const OUString& rText) override;
+    virtual void do_set_active(int pos) override;
+    virtual void do_set_active_id(const OUString& rText) override;
     virtual bool changed_by_direct_pick() const override;
 
     // OnDemandRenderingHandler
diff --git a/vcl/inc/qt5/QtInstanceComboBox.hxx 
b/vcl/inc/qt5/QtInstanceComboBox.hxx
index d73a08534be6..adcdf15a3774 100644
--- a/vcl/inc/qt5/QtInstanceComboBox.hxx
+++ b/vcl/inc/qt5/QtInstanceComboBox.hxx
@@ -35,7 +35,7 @@ public:
     virtual void clear() override;
 
     virtual int get_active() const override;
-    virtual void set_active(int nPos) override;
+    virtual void do_set_active(int nPos) override;
     virtual void remove(int nPos) override;
 
     virtual OUString get_active_text() const override;
@@ -43,7 +43,7 @@ public:
     virtual int find_text(const OUString& rStr) const override;
 
     virtual OUString get_active_id() const override;
-    virtual void set_active_id(const OUString& rStr) override;
+    virtual void do_set_active_id(const OUString& rStr) override;
     virtual OUString get_id(int nPos) const override;
     virtual void set_id(int nRow, const OUString& rId) override;
     virtual int find_id(const OUString& rId) const override;
diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx
index 5330bd06e674..797a1ad154dc 100644
--- a/vcl/inc/salvtables.hxx
+++ b/vcl/inc/salvtables.hxx
@@ -760,7 +760,7 @@ public:
         return *pRet;
     }
 
-    virtual void set_active_id(const OUString& rStr) override
+    virtual void do_set_active_id(const OUString& rStr) override
     {
         for (int i = 0; i < get_count(); ++i)
         {
@@ -772,7 +772,7 @@ public:
         }
     }
 
-    virtual void set_active(int pos) override
+    virtual void do_set_active(int pos) override
     {
         assert(m_xComboBox->IsUpdateMode()
                && "don't set_active when frozen, set_active after thaw. Note 
selection doesn't "
diff --git a/vcl/jsdialog/jsdialogbuilder.cxx b/vcl/jsdialog/jsdialogbuilder.cxx
index 0e78c925ba3b..968a762b9b21 100644
--- a/vcl/jsdialog/jsdialogbuilder.cxx
+++ b/vcl/jsdialog/jsdialogbuilder.cxx
@@ -1260,9 +1260,9 @@ void JSListBox::remove(int pos)
     sendUpdate();
 }
 
-void JSListBox::set_active(int pos)
+void JSListBox::do_set_active(int pos)
 {
-    SalInstanceComboBoxWithoutEdit::set_active(pos);
+    SalInstanceComboBoxWithoutEdit::do_set_active(pos);
     sendUpdate();
 }
 
@@ -1301,12 +1301,12 @@ void JSComboBox::set_entry_text(const OUString& rText)
     sendAction(std::move(pMap));
 }
 
-void JSComboBox::set_active(int pos)
+void JSComboBox::do_set_active(int pos)
 {
     if (pos == get_active())
         return;
 
-    SalInstanceComboBoxWithEdit::set_active(pos);
+    SalInstanceComboBoxWithEdit::do_set_active(pos);
 
     std::unique_ptr<jsdialog::ActionDataMap> pMap = 
std::make_unique<jsdialog::ActionDataMap>();
     (*pMap)[ACTION_TYPE ""_ostr] = "select";
@@ -1314,10 +1314,10 @@ void JSComboBox::set_active(int pos)
     sendAction(std::move(pMap));
 }
 
-void JSComboBox::set_active_id(const OUString& rStr)
+void JSComboBox::do_set_active_id(const OUString& rStr)
 {
     sal_uInt16 nPos = find_id(rStr);
-    set_active(nPos);
+    do_set_active(nPos);
 }
 
 bool JSComboBox::changed_by_direct_pick() const { return true; }
diff --git a/vcl/qt5/QtInstanceComboBox.cxx b/vcl/qt5/QtInstanceComboBox.cxx
index 68699ef4e205..bb4f7f6666d7 100644
--- a/vcl/qt5/QtInstanceComboBox.cxx
+++ b/vcl/qt5/QtInstanceComboBox.cxx
@@ -118,7 +118,7 @@ int QtInstanceComboBox::get_active() const
     return nCurrentIndex;
 }
 
-void QtInstanceComboBox::set_active(int nPos)
+void QtInstanceComboBox::do_set_active(int nPos)
 {
     SolarMutexGuard g;
     GetQtInstance().RunInMainThread([&] { m_pComboBox->setCurrentIndex(nPos); 
});
@@ -168,7 +168,7 @@ OUString QtInstanceComboBox::get_active_id() const
     return sId;
 }
 
-void QtInstanceComboBox::set_active_id(const OUString& rId)
+void QtInstanceComboBox::do_set_active_id(const OUString& rId)
 {
     SolarMutexGuard g;
 
diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx
index 2430b98b155b..ffab77148148 100644
--- a/vcl/unx/gtk3/gtkinst.cxx
+++ b/vcl/unx/gtk3/gtkinst.cxx
@@ -20356,9 +20356,9 @@ public:
         return nActive != -1 ? get_id(nActive) : OUString();
     }
 
-    virtual void set_active_id(const OUString& rStr) override
+    virtual void do_set_active_id(const OUString& rStr) override
     {
-        set_active(find_id(rStr));
+        do_set_active(find_id(rStr));
         m_bChangedByMenu = false;
     }
 
@@ -20399,7 +20399,7 @@ public:
         gtk_widget_set_size_request(m_pWidget, nWidth, nHeight);
     }
 
-    virtual void set_active(int pos) override
+    virtual void do_set_active(int pos) override
     {
         set_active_including_mru(include_mru(pos), false);
     }
@@ -22257,9 +22257,9 @@ public:
         return nActive != -1 ? get_id(nActive) : OUString();
     }
 
-    virtual void set_active_id(const OUString& rStr) override
+    virtual void do_set_active_id(const OUString& rStr) override
     {
-        set_active(find_id(rStr));
+        do_set_active(find_id(rStr));
         m_bChangedByMenu = false;
     }
 
@@ -22300,7 +22300,7 @@ public:
         gtk_widget_set_size_request(m_pWidget, nWidth, nHeight);
     }
 
-    virtual void set_active(int pos) override
+    virtual void do_set_active(int pos) override
     {
         set_active_including_mru(include_mru(pos), false);
     }

Reply via email to