include/svtools/ctrlbox.hxx            |   10 +-
 sc/qa/uitest/calc_tests/formatCells.py |   13 ++-
 svtools/source/control/ctrlbox.cxx     |  131 +++++++++++++++++++++++++++------
 svtools/uiconfig/ui/linewindow.ui      |   20 ++++-
 svx/source/tbxctrls/tbcontrl.cxx       |    8 ++
 vcl/jsdialog/executor.cxx              |   23 ++++-
 6 files changed, 162 insertions(+), 43 deletions(-)

New commits:
commit 92da7d3c6e8de077b35c20ff748752f32e1a8590
Author:     Parth Raiyani <[email protected]>
AuthorDate: Mon Dec 29 18:02:40 2025 +0530
Commit:     Szymon Kłos <[email protected]>
CommitDate: Mon Jan 5 10:44:18 2026 +0100

    iconview: Replaces valueset with iconview widget in line pop-up
    
    - Updated the UI layout to use GtkIconView
    - Replaced valueset logic with relevant iconview logic
    - Added tooltip support
    - Fixed potential LibreOffice Kit crash by disabling unnecessary tooltip 
for popup dialog in Libreoffit Kit
    - Fixed relevant test cases
    
    Change-Id: I3c89c88e435fb8803b863db6b1295d8c27a2a8cb
    Signed-off-by: Parth Raiyani <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196150
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Szymon Kłos <[email protected]>

diff --git a/include/svtools/ctrlbox.hxx b/include/svtools/ctrlbox.hxx
index 1ea54f44308d..1ea6d06187a3 100644
--- a/include/svtools/ctrlbox.hxx
+++ b/include/svtools/ctrlbox.hxx
@@ -197,8 +197,6 @@ inline Color sameDistColor( Color /*rMain*/, Color rDefault 
)
     return rDefault;
 }
 
-class ValueSet;
-
 class SVT_DLLPUBLIC SvtLineListBox final : public WeldToolbarPopup
 {
 public:
@@ -253,7 +251,8 @@ private:
                                     Color nColor1, Color nColor2, Color 
nColorDist,
                                     SvxBorderLineStyle nStyle, BitmapEx& rBmp 
);
 
-    DECL_DLLPRIVATE_LINK(ValueSelectHdl, ValueSet*, void);
+    DECL_DLLPRIVATE_LINK(SelectionChangedHdl, weld::IconView&, void);
+    DECL_DLLPRIVATE_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString);
     DECL_DLLPRIVATE_LINK(ToggleHdl, weld::Toggleable&, void);
     DECL_DLLPRIVATE_LINK(NoneHdl, weld::Button&, void);
     DECL_DLLPRIVATE_LINK(StyleUpdatedHdl, weld::Widget&, void);
@@ -261,14 +260,15 @@ private:
     void            UpdateEntries();
 
     void UpdatePreview();
+    static VclPtr<VirtualDevice> GetVirtualDevice(const BitmapEx& rBmp);
+    ImpLineListData* getBorderLineData(sal_Int32 nId);
 
                     SvtLineListBox( const SvtLineListBox& ) = delete;
     SvtLineListBox&    operator =( const SvtLineListBox& ) = delete;
 
     std::unique_ptr<weld::MenuButton> m_xControl;
     std::unique_ptr<weld::Button> m_xNoneButton;
-    std::unique_ptr<ValueSet> m_xLineSet;
-    std::unique_ptr<weld::CustomWeld> m_xLineSetWin;
+    std::unique_ptr<weld::IconView> m_xLineIV;
 
     std::vector<std::unique_ptr<ImpLineListData>> m_vLineList;
     tools::Long            m_nWidth;
diff --git a/sc/qa/uitest/calc_tests/formatCells.py 
b/sc/qa/uitest/calc_tests/formatCells.py
index e00f6d0fe01a..dd11acae25e3 100644
--- a/sc/qa/uitest/calc_tests/formatCells.py
+++ b/sc/qa/uitest/calc_tests/formatCells.py
@@ -261,21 +261,23 @@ class formatCell(UITestCase):
                     select_pos(xTabs, "5")  #tab Borders
 
                     linewidthmf = xDialog.getChild("linewidthmf")
-                    xLineSet = xDialog.getChild('lineset')
+                    xLineIconView = xDialog.getChild('iconview_lines')
 
                     # check line-width for default solid line
-                    self.assertEqual('0', 
get_state_as_dict(xLineSet)['SelectedItemPos'])
+                    self.assertEqual('0', 
get_state_as_dict(xLineIconView)['SelectedItemPos'])
                     widthVal = get_state_as_dict(linewidthmf)["Text"]
                     self.assertEqual(widthVal, '0.75 pt')
 
                     # set line style to "double" (minimal width is taken)
-                    xLineSet.executeAction("CHOOSE", mkPropertyValues({"POS": 
'16'}))
+                    element6 = xLineIconView.getChild('6')
+                    element6.executeAction("SELECT", mkPropertyValues({}))
                     widthVal = get_state_as_dict(linewidthmf)["Text"]
                     # minimum predefined width is Medium (1.50 pt)
                     self.assertEqual(widthVal, '1.50 pt')
 
                     # set line style to "solid"
-                    xLineSet.executeAction("CHOOSE", mkPropertyValues({"POS": 
"1"}))
+                    element1 = xLineIconView.getChild('0')
+                    element1.executeAction("SELECT", mkPropertyValues({}))
                     widthVal = get_state_as_dict(linewidthmf)["Text"]
                     self.assertEqual(widthVal, '1.50 pt')
 
@@ -287,7 +289,8 @@ class formatCell(UITestCase):
                     self.assertEqual(widthVal, '2.25 pt')
 
                     # set line style to "double" (minimal width is not taken)
-                    xLineSet.executeAction("CHOOSE", mkPropertyValues({"POS": 
"8"}))
+                    element8 = xLineIconView.getChild('6')
+                    element8.executeAction("SELECT", mkPropertyValues({}))
                     widthVal = get_state_as_dict(linewidthmf)["Text"]
                     self.assertEqual(widthVal, '2.25 pt')
 
diff --git a/svtools/source/control/ctrlbox.cxx 
b/svtools/source/control/ctrlbox.cxx
index 736de5be4850..8789b9066d9d 100644
--- a/svtools/source/control/ctrlbox.cxx
+++ b/svtools/source/control/ctrlbox.cxx
@@ -47,7 +47,6 @@
 #include <svtools/ctrlbox.hxx>
 #include <svtools/ctrltool.hxx>
 #include <svtools/borderhelper.hxx>
-#include <svtools/valueset.hxx>
 
 #include <basegfx/polygon/b2dpolygon.hxx>
 #include <basegfx/polygon/b2dpolygontools.hxx>
@@ -1415,9 +1414,10 @@ int FontSizeBox::get_value() const
 
 SvxBorderLineStyle SvtLineListBox::GetSelectEntryStyle() const
 {
-    if (m_xLineSet->IsNoSelection())
+    if (m_xLineIV->get_selected_id().isEmpty())
         return SvxBorderLineStyle::NONE;
-    auto nId = m_xLineSet->GetSelectedItemId();
+
+    auto nId = m_xLineIV->get_selected_id().toInt32();
     return static_cast<SvxBorderLineStyle>(nId - 1);
 }
 
@@ -1489,17 +1489,20 @@ 
SvtLineListBox::SvtLineListBox(std::unique_ptr<weld::MenuButton> pControl)
     : WeldToolbarPopup(css::uno::Reference<css::frame::XFrame>(), 
pControl.get(), u"svt/ui/linewindow.ui"_ustr, u"line_popup_window"_ustr)
     , m_xControl(std::move(pControl))
     , m_xNoneButton(m_xBuilder->weld_button(u"none_line_button"_ustr))
-    , m_xLineSet(new ValueSet(nullptr))
-    , m_xLineSetWin(new weld::CustomWeld(*m_xBuilder, u"lineset"_ustr, 
*m_xLineSet))
+    , m_xLineIV(m_xBuilder->weld_icon_view(u"iconview_lines"_ustr))
     , m_nWidth( 5 )
     , aVirDev(VclPtr<VirtualDevice>::Create())
     , aColor(COL_BLACK)
 {
-    const StyleSettings& rStyleSettings = 
Application::GetSettings().GetStyleSettings();
-    m_xLineSet->SetStyle(WinBits(WB_FLATVALUESET | WB_NO_DIRECTSELECT | 
WB_TABSTOP));
-    
m_xLineSet->SetItemHeight(rStyleSettings.GetListBoxPreviewDefaultPixelSize().Height()
 + 1);
-    m_xLineSet->SetColCount(1);
-    m_xLineSet->SetSelectHdl(LINK(this, SvtLineListBox, ValueSelectHdl));
+    m_xLineIV->connect_selection_changed(LINK(this, SvtLineListBox, 
SelectionChangedHdl));
+
+    // Avoid LibreOffice Kit crash: tooltip handlers cause segfault during 
JSDialog
+    // serialization when popup widgets are destroyed/recreated during 
character formatting resets.
+    // Tooltip event binding is not needed for LibreOffice Kit
+    if (!comphelper::LibreOfficeKit::isActive())
+    {
+        m_xLineIV->connect_query_tooltip(LINK(this, SvtLineListBox, 
QueryTooltipHdl));
+    }
 
     m_xNoneButton->connect_clicked(LINK(this, SvtLineListBox, NoneHdl));
 
@@ -1528,7 +1531,7 @@ void SvtLineListBox::GrabFocus()
     if (GetSelectEntryStyle() == SvxBorderLineStyle::NONE)
         m_xNoneButton->grab_focus();
     else
-        m_xLineSet->GrabFocus();
+        m_xLineIV->grab_focus();
 }
 
 IMPL_LINK(SvtLineListBox, ToggleHdl, weld::Toggleable&, rButton, void)
@@ -1546,7 +1549,7 @@ IMPL_LINK_NOARG(SvtLineListBox, StyleUpdatedHdl, 
weld::Widget&, void)
 IMPL_LINK_NOARG(SvtLineListBox, NoneHdl, weld::Button&, void)
 {
     SelectEntry(SvxBorderLineStyle::NONE);
-    ValueSelectHdl(nullptr);
+    SelectionChangedHdl(*m_xLineIV);
 }
 
 SvtLineListBox::~SvtLineListBox()
@@ -1570,9 +1573,26 @@ OUString 
SvtLineListBox::GetLineStyleName(SvxBorderLineStyle eStyle)
 void SvtLineListBox::SelectEntry(SvxBorderLineStyle nStyle)
 {
     if (nStyle == SvxBorderLineStyle::NONE)
-        m_xLineSet->SetNoSelection();
+        m_xLineIV->unselect_all();
     else
-        m_xLineSet->SelectItem(static_cast<sal_Int16>(nStyle) + 1);
+    {
+        for (int i = 0; i < m_xLineIV->n_children(); i++)
+        {
+            OUString sId = m_xLineIV->get_id(i);
+
+            if (!sId.isEmpty())
+            {
+                sal_Int32 nId = sId.toUInt32();
+                SvxBorderLineStyle eIdStyle = 
static_cast<SvxBorderLineStyle>(nId - 1);
+
+                if (eIdStyle == nStyle)
+                {
+                    m_xLineIV->select(i);
+                    break;
+                }
+            }
+        }
+    }
     UpdatePreview();
 }
 
@@ -1589,7 +1609,7 @@ void SvtLineListBox::UpdateEntries()
     SvxBorderLineStyle eSelected = GetSelectEntryStyle();
 
     // Remove the old entries
-    m_xLineSet->Clear();
+    m_xLineIV->clear();
 
     const StyleSettings& rSettings = 
Application::GetSettings().GetStyleSettings();
     Color aFieldColor = rSettings.GetFieldColor();
@@ -1608,17 +1628,39 @@ void SvtLineListBox::UpdateEntries()
                 pData->GetColorLine2(aColor),
                 pData->GetColorDist(aColor, aFieldColor),
                 pData->GetStyle(), aBmp );
-        sal_Int16 nItemId = static_cast<sal_Int16>(pData->GetStyle()) + 1;
-        m_xLineSet->InsertItem(nItemId, Image(aBmp), 
GetLineStyleName(pData->GetStyle()));
+        sal_Int16 nPos = static_cast<sal_Int16>(pData->GetStyle());
+        VclPtr<VirtualDevice> pVDev = GetVirtualDevice(aBmp);
+
+        OUString sId = OUString::number(nPos + 1);
+        m_xLineIV->append(sId, GetLineStyleName(pData->GetStyle()), 
pVDev.get());
         if (pData->GetStyle() == eSelected)
-            m_xLineSet->SelectItem(nItemId);
+            m_xLineIV->select(n);
         n++;
     }
+}
+
+VclPtr<VirtualDevice> SvtLineListBox::GetVirtualDevice(const BitmapEx& rBmp)
+{
+    constexpr tools::Long nMarginTopBottom = 5;
+    constexpr tools::Long nMarginLeftRight = 2;
+
+    Size aBmpSize = rBmp.GetSizePixel();
+
+    Size aVDSize(
+        aBmpSize.Width()  + 2 * nMarginLeftRight,
+        aBmpSize.Height() + 2 * nMarginTopBottom
+    );
 
-    m_xLineSet->SetOptimalSize();
+    VclPtr<VirtualDevice> pVDev = VclPtr<VirtualDevice>::Create();
+    pVDev->SetOutputSizePixel(aVDSize);
+
+    Point aDrawPos(nMarginLeftRight, nMarginTopBottom);
+    pVDev->DrawBitmapEx(aDrawPos, rBmp);
+
+    return pVDev;
 }
 
-IMPL_LINK_NOARG(SvtLineListBox, ValueSelectHdl, ValueSet*, void)
+IMPL_LINK_NOARG(SvtLineListBox, SelectionChangedHdl, weld::IconView&, void)
 {
     maSelectHdl.Call(*this);
     UpdatePreview();
@@ -1626,6 +1668,34 @@ IMPL_LINK_NOARG(SvtLineListBox, ValueSelectHdl, 
ValueSet*, void)
         m_xControl->set_active(false);
 }
 
+IMPL_LINK(SvtLineListBox, QueryTooltipHdl, const weld::TreeIter&, rIter, 
OUString)
+{
+    OUString sId = m_xLineIV->get_id(rIter);
+    if (sId.isEmpty())
+        return OUString();
+
+    sal_Int32 nId = sId.toUInt32();
+
+    ImpLineListData* pData = getBorderLineData(nId - 1);
+    if (pData)
+        return GetLineStyleName(pData->GetStyle());
+
+    return OUString();
+}
+
+ImpLineListData* SvtLineListBox::getBorderLineData(sal_Int32 nId)
+{
+    SvxBorderLineStyle eStyle = static_cast<SvxBorderLineStyle>(nId);
+
+    for (auto& pData : m_vLineList)
+    {
+        if (pData->GetStyle() == eStyle)
+            return pData.get();
+    }
+
+    return nullptr;
+}
+
 void SvtLineListBox::UpdatePreview()
 {
     SvxBorderLineStyle eStyle = GetSelectEntryStyle();
@@ -1645,13 +1715,28 @@ void SvtLineListBox::UpdatePreview()
     }
     else
     {
-        Image 
aImage(m_xLineSet->GetItemImage(m_xLineSet->GetSelectedItemId()));
+        const StyleSettings& rSettings = 
Application::GetSettings().GetStyleSettings();
+        Color aFieldColor = rSettings.GetFieldColor();
+
+        auto nId = m_xLineIV->get_selected_id().toInt32();
+        ImpLineListData* pData = getBorderLineData(nId - 1);
+
+        if(!pData) return;
+
+        BitmapEx aBmp;
+        ImpGetLine( pData->GetLine1ForWidth( m_nWidth ),
+                pData->GetLine2ForWidth( m_nWidth ),
+                pData->GetDistForWidth( m_nWidth ),
+                pData->GetColorLine1(aColor),
+                pData->GetColorLine2(aColor),
+                pData->GetColorDist(aColor, aFieldColor),
+                pData->GetStyle(), aBmp );
+        Image aImage(aBmp);
         m_xControl->set_label(u""_ustr);
         const auto nPos = (aVirDev->GetOutputSizePixel().Height() - 
aImage.GetSizePixel().Height()) / 2;
         aVirDev->Push(vcl::PushFlags::MAPMODE);
         aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
-        const StyleSettings& rSettings = 
Application::GetSettings().GetStyleSettings();
-        aVirDev->SetBackground(rSettings.GetFieldColor());
+        aVirDev->SetBackground(aFieldColor);
         aVirDev->Erase();
         aVirDev->DrawImage(Point(0, nPos), aImage);
         m_xControl->set_image(aVirDev.get());
diff --git a/svtools/uiconfig/ui/linewindow.ui 
b/svtools/uiconfig/ui/linewindow.ui
index 932642998118..eb71c2bb25a8 100644
--- a/svtools/uiconfig/ui/linewindow.ui
+++ b/svtools/uiconfig/ui/linewindow.ui
@@ -7,6 +7,14 @@
     <property name="can-focus">False</property>
     <property name="icon-name">cmd/sc_square_unfilled.png</property>
   </object>
+  <object class="GtkTreeStore" id="liststore1">
+    <columns>
+      <!-- column-name pixbuf -->
+      <column type="GdkPixbuf"/>
+      <!-- column-name id -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
   <object class="GtkPopover" id="line_popup_window">
     <property name="can-focus">False</property>
     <property name="no-show-all">True</property>
@@ -37,13 +45,19 @@
           </packing>
         </child>
         <child>
-          <object class="GtkDrawingArea" id="lineset">
+          <object class="GtkIconView" id="iconview_lines">
             <property name="visible">True</property>
             <property name="can-focus">True</property>
-            <property name="receives-default">True</property>
-            <property name="events">GDK_BUTTON_PRESS_MASK | 
GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | 
GDK_STRUCTURE_MASK</property>
             <property name="hexpand">True</property>
             <property name="vexpand">True</property>
+            <property name="model">liststore1</property>
+            <property name="pixbuf-column">0</property>
+            <property name="selection-mode">single</property>
+            <property name="columns">1</property>
+            <property name="activate-on-single-click">True</property>
+            <property name="item-padding">2</property>
+            <property name="row-spacing">2</property>
+            <property name="margin">2</property>
           </object>
           <packing>
             <property name="expand">False</property>
commit 7501228f69a492278fb3e982b7abd87c759f3c28
Author:     Parth Raiyani <[email protected]>
AuthorDate: Mon Jan 5 12:31:35 2026 +0530
Commit:     Szymon Kłos <[email protected]>
CommitDate: Mon Jan 5 10:44:15 2026 +0100

    Enhance ExecuteAction to handle mouse events and key modifiers in IconView
    
    - There are some iconviews that needs to know about shift or ctrl keys 
press along with selection/activation action to behavior differently
    - i.e. sidebar -> border popup dialog overwrite borders if shift key is 
pressed while activating an item and normally applies the border if not key is 
pressed
    - To handle this behavior, we need these information(mousepressed, 
shiftkey, ctrlkey) from libreofficekit instance
    - This change parse this information and trigger mouse press to achieve 
that behavior
    
    Change-Id: I9568eb81e329d60ee647488fe751043a755eb817
    Signed-off-by: Parth Raiyani <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194688
    Reviewed-by: Szymon Kłos <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/svx/source/tbxctrls/tbcontrl.cxx b/svx/source/tbxctrls/tbcontrl.cxx
index 3d1fa2f92ff4..b9c99fd425ac 100644
--- a/svx/source/tbxctrls/tbcontrl.cxx
+++ b/svx/source/tbxctrls/tbcontrl.cxx
@@ -481,6 +481,7 @@ private:
     DECL_LINK( MousePressHdl, const MouseEvent&, bool );
     DECL_LINK( MouseReleaseHdl, const MouseEvent&, bool );
     DECL_LINK(KeyPressHdl, const KeyEvent&, bool);
+    DECL_LINK(KeyReleaseHdl, const KeyEvent&, bool);
     DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString);
     OUString GetBorderTypeTooltip(sal_uInt16 nBorderType) const;
 
@@ -2606,6 +2607,7 @@ 
SvxFrameWindow_Impl::SvxFrameWindow_Impl(SvxFrameToolBoxControl* pControl, weld:
     mxFrameIV->connect_mouse_press(LINK(this, SvxFrameWindow_Impl, 
MousePressHdl));
     mxFrameIV->connect_mouse_release(LINK(this, SvxFrameWindow_Impl, 
MouseReleaseHdl));
     mxFrameIV->connect_key_press(LINK(this, SvxFrameWindow_Impl, KeyPressHdl));
+    mxFrameIV->connect_key_release(LINK(this, SvxFrameWindow_Impl, 
KeyReleaseHdl));
     mxFrameIV->connect_item_activated( LINK( this, SvxFrameWindow_Impl, 
SelectHdl ) );
 
     // Avoid LibreOffice Kit crash: tooltip handlers cause segfault during 
JSDialog
@@ -2846,6 +2848,12 @@ IMPL_LINK(SvxFrameWindow_Impl, KeyPressHdl, const 
KeyEvent&, rKEvt, bool)
     return false;
 }
 
+IMPL_LINK(SvxFrameWindow_Impl, KeyReleaseHdl, const KeyEvent&, /*rKEvt*/, bool)
+{
+    nModifier = 0;
+    return true;
+}
+
 VclPtr<VirtualDevice> SvxFrameWindow_Impl::GetVirtualDevice(BitmapEx 
aPreviewBitmap)
 {
     VclPtr<VirtualDevice> pVDev = VclPtr<VirtualDevice>::Create();
diff --git a/vcl/jsdialog/executor.cxx b/vcl/jsdialog/executor.cxx
index 080e93d82a6f..e53fd13ea7d3 100644
--- a/vcl/jsdialog/executor.cxx
+++ b/vcl/jsdialog/executor.cxx
@@ -687,10 +687,23 @@ bool ExecuteAction(const OUString& nWindowId, const 
OUString& rWidget, const Str
             auto pIconView = dynamic_cast<weld::IconView*>(pWidget);
             if (pIconView)
             {
-                if (sAction == "select")
+                sal_Int32 nPos = o3tl::toInt32(rData.at(u"data"_ustr));
+                if (sAction == "keypress")
+                {
+                    sal_uInt32 nKeyNo = rData.at(u"data"_ustr).toUInt32();
+                    LOKTrigger::trigger_key_press(*pIconView,
+                                                  KeyEvent(nKeyNo, 
vcl::KeyCode(nKeyNo)));
+                    return true;
+                }
+                else if (sAction == "keyrelease")
+                {
+                    sal_uInt32 nKeyNo = rData.at(u"data"_ustr).toUInt32();
+                    LOKTrigger::trigger_key_release(*pIconView,
+                                                    KeyEvent(nKeyNo, 
vcl::KeyCode(nKeyNo)));
+                    return true;
+                }
+                else if (sAction == "select")
                 {
-                    sal_Int32 nPos = o3tl::toInt32(rData.at(u"data"_ustr));
-
                     pIconView->select(nPos);
                     LOKTrigger::trigger_changed(*pIconView);
 
@@ -698,8 +711,6 @@ bool ExecuteAction(const OUString& nWindowId, const 
OUString& rWidget, const Str
                 }
                 else if (sAction == "activate")
                 {
-                    sal_Int32 nPos = o3tl::toInt32(rData.at(u"data"_ustr));
-
                     pIconView->select(nPos);
                     LOKTrigger::trigger_changed(*pIconView);
                     LOKTrigger::trigger_item_activated(*pIconView);
@@ -708,8 +719,6 @@ bool ExecuteAction(const OUString& nWindowId, const 
OUString& rWidget, const Str
                 }
                 else if (sAction == "contextmenu")
                 {
-                    sal_Int32 nPos = o3tl::toInt32(rData.at(u"data"_ustr));
-
                     tools::Rectangle aRect = pIconView->get_rect(nPos);
                     Point aPoint = aRect.Center();
                     assert(aPoint.getX() >= 0 && aPoint.getY() >= 0);

Reply via email to