summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2019-10-21 14:20:12 +0100
committerCaolán McNamara <caolanm@redhat.com>2019-10-29 13:58:47 +0100
commit09e3d45cdc5c739e5246388a83ccfc6d76bf66e9 (patch)
tree5ae533f38a626016951b02e7d2406c368b298723 /vcl
parent93a641d291adf86491cc68ac64f4f614c937183a (diff)
weld fpicker cluster
Change-Id: I6566263809ff0032388a0b56571f0cf7428058d7 Reviewed-on: https://gerrit.libreoffice.org/81334 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolanm@redhat.com> Tested-by: Caolán McNamara <caolanm@redhat.com>
Diffstat (limited to 'vcl')
-rw-r--r--vcl/Library_vcl.mk2
-rw-r--r--vcl/inc/iconview.hxx39
-rw-r--r--vcl/inc/iconviewimpl.hxx68
-rw-r--r--vcl/inc/treeglue.hxx24
-rw-r--r--vcl/source/app/salvtables.cxx313
-rw-r--r--vcl/source/treelist/iconview.cxx224
-rw-r--r--vcl/source/treelist/iconviewimpl.cxx662
-rw-r--r--vcl/source/window/builder.cxx24
-rw-r--r--vcl/unx/gtk3/gtk3gtkinst.cxx496
9 files changed, 1810 insertions, 42 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 7cb6c270fd59..00b98cdb2929 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -243,6 +243,8 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/source/outdev/nativecontrols \
vcl/source/outdev/map \
vcl/source/treelist/headbar \
+ vcl/source/treelist/iconview \
+ vcl/source/treelist/iconviewimpl \
vcl/source/treelist/imap \
vcl/source/treelist/imap2 \
vcl/source/treelist/imap3 \
diff --git a/vcl/inc/iconview.hxx b/vcl/inc/iconview.hxx
new file mode 100644
index 000000000000..750f7ec6ebf0
--- /dev/null
+++ b/vcl/inc/iconview.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVTOOLS_ICONVIEW_HXX
+#define INCLUDED_SVTOOLS_ICONVIEW_HXX
+
+#include <vcl/treelistbox.hxx>
+
+class IconView : public SvTreeListBox
+{
+public:
+ IconView( vcl::Window* pParent, WinBits nBits );
+
+ virtual void Resize() override;
+
+ virtual tools::Rectangle GetFocusRect( SvTreeListEntry*, long nEntryPos ) override;
+
+ void PaintEntry( SvTreeListEntry&, long nX, long nY, vcl::RenderContext& rRenderContext);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/iconviewimpl.hxx b/vcl/inc/iconviewimpl.hxx
new file mode 100644
index 000000000000..df11f5952426
--- /dev/null
+++ b/vcl/inc/iconviewimpl.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_INC_ICONVIEWIMPL_HXX
+#define INCLUDED_VCL_SOURCE_INC_ICONVIEWIMPL_HXX
+
+#include <vcl/svimpbox.hxx>
+
+class SvTreeListBox;
+class Point;
+
+class IconViewImpl : public SvImpLBox
+{
+public:
+ IconViewImpl( SvTreeListBox* pTreeListBox, SvTreeList* pTreeList, WinBits nWinStyle );
+
+ void KeyDown( bool bPageDown ) override;
+
+ void KeyUp( bool bPageUp ) override;
+
+ Point GetEntryPosition( SvTreeListEntry* pEntry ) const override;
+
+ SvTreeListEntry* GetClickedEntry( const Point& rPoint ) const override;
+
+ bool IsEntryInView( SvTreeListEntry* pEntry ) const override;
+
+ void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
+
+ // returns 0 if position is just past the last entry
+ SvTreeListEntry* GetEntry( const Point& rPoint ) const override;
+
+ void UpdateAll( bool bInvalidateCompleteView ) override;
+
+ bool KeyInput( const KeyEvent& ) override;
+
+ void InvalidateEntry( long nId ) const override;
+
+protected:
+ long GetEntryLine( SvTreeListEntry* pEntry ) const override;
+
+ void CursorUp() override;
+ void CursorDown() override;
+ void PageDown( sal_uInt16 nDelta ) override;
+ void PageUp( sal_uInt16 nDelta ) override;
+
+ void SyncVerThumb() override;
+ void AdjustScrollBars( Size& rSize ) override;
+};
+
+#endif // INCLUDED_VCL_SOURCE_INC_ICONVIEWIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/treeglue.hxx b/vcl/inc/treeglue.hxx
index 05ebee523af7..d67ce09d6d96 100644
--- a/vcl/inc/treeglue.hxx
+++ b/vcl/inc/treeglue.hxx
@@ -17,16 +17,40 @@
class LclHeaderTabListBox : public SvHeaderTabListBox
{
+private:
+ Link<SvTreeListEntry*, bool> m_aEditingEntryHdl;
+ Link<std::pair<SvTreeListEntry*, OUString>, bool> m_aEditedEntryHdl;
+
public:
LclHeaderTabListBox(vcl::Window* pParent, WinBits nWinStyle)
: SvHeaderTabListBox(pParent, nWinStyle)
{
}
+ void SetEditingEntryHdl(const Link<SvTreeListEntry*, bool>& rLink)
+ {
+ m_aEditingEntryHdl = rLink;
+ }
+
+ void SetEditedEntryHdl(const Link<std::pair<SvTreeListEntry*, OUString>, bool>& rLink)
+ {
+ m_aEditedEntryHdl = rLink;
+ }
+
virtual DragDropMode NotifyStartDrag(TransferDataContainer&, SvTreeListEntry*) override
{
return GetDragDropMode();
}
+
+ virtual bool EditingEntry(SvTreeListEntry* pEntry, Selection&) override
+ {
+ return m_aEditingEntryHdl.Call(pEntry);
+ }
+
+ virtual bool EditedEntry(SvTreeListEntry* pEntry, const OUString& rNewText) override
+ {
+ return m_aEditedEntryHdl.Call(std::pair<SvTreeListEntry*, OUString>(pEntry, rNewText));
+ }
};
class LclTabListBox : public SvTabListBox
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index ef81c7f91d3c..6b4dcd0a4032 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -20,6 +20,7 @@
#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
#include <com/sun/star/awt/XWindow.hpp>
#include <officecfg/Office/Common.hxx>
+#include <iconview.hxx>
#include <salframe.hxx>
#include <salinst.hxx>
#include <salvd.hxx>
@@ -914,6 +915,10 @@ public:
auto nInsertPos = pos == -1 ? MENU_APPEND : pos;
m_xMenu->InsertSeparator(rId.toUtf8(), nInsertPos);
}
+ PopupMenu* getMenu() const
+ {
+ return m_xMenu.get();
+ }
virtual ~SalInstanceMenu() override
{
m_xMenu->SetSelectHdl(Link<::Menu*, bool>());
@@ -933,6 +938,7 @@ class SalInstanceToolbar : public SalInstanceWidget, public virtual weld::Toolba
private:
VclPtr<ToolBox> m_xToolBox;
std::map<sal_uInt16, VclPtr<vcl::Window>> m_aFloats;
+ std::map<sal_uInt16, VclPtr<PopupMenu>> m_aMenus;
DECL_LINK(ClickHdl, ToolBox*, void);
DECL_LINK(DropdownClick, ToolBox*, void);
@@ -963,12 +969,24 @@ public:
if (m_xToolBox->GetItemBits(nItemId) & ToolBoxItemBits::DROPDOWN)
{
auto pFloat = m_aFloats[nItemId];
- if (!pFloat)
- return;
- if (bActive)
- vcl::Window::GetDockingManager()->StartPopupMode(m_xToolBox, pFloat, FloatWinPopupFlags::GrabFocus);
- else
- vcl::Window::GetDockingManager()->EndPopupMode(pFloat);
+ if (pFloat)
+ {
+ if (bActive)
+ vcl::Window::GetDockingManager()->StartPopupMode(m_xToolBox, pFloat, FloatWinPopupFlags::GrabFocus);
+ else
+ vcl::Window::GetDockingManager()->EndPopupMode(pFloat);
+ }
+ auto pPopup = m_aMenus[nItemId];
+ if (pPopup)
+ {
+ if (bActive)
+ {
+ tools::Rectangle aRect = m_xToolBox->GetItemRect(nItemId);
+ pPopup->Execute(m_xToolBox, aRect, PopupMenuFlags::ExecuteDown);
+ }
+ else
+ pPopup->EndExecute();
+ }
}
}
@@ -985,7 +1003,20 @@ public:
if (pFloat)
pFloat->EnableDocking();
- m_aFloats[m_xToolBox->GetItemId(OUString::fromUtf8(rIdent))] = pFloat;
+ sal_uInt16 nId = m_xToolBox->GetItemId(OUString::fromUtf8(rIdent));
+ m_aFloats[nId] = pFloat;
+ m_aMenus[nId] = nullptr;
+ }
+
+ virtual void set_item_menu(const OString& rIdent, weld::Menu* pMenu) override
+ {
+ SalInstanceMenu* pInstanceMenu = dynamic_cast<SalInstanceMenu*>(pMenu);
+
+ PopupMenu* pPopup = pInstanceMenu? pInstanceMenu->getMenu() : nullptr;
+
+ sal_uInt16 nId = m_xToolBox->GetItemId(OUString::fromUtf8(rIdent));
+ m_aMenus[nId] = pPopup;
+ m_aFloats[nId] = nullptr;
}
virtual void insert_separator(int pos, const OUString& /*rId*/) override
@@ -2961,6 +2992,11 @@ public:
{
m_xImage->SetImage(createImage(*pDevice));
}
+
+ virtual void set_image(const css::uno::Reference<css::graphic::XGraphic>& rImage) override
+ {
+ m_xImage->SetImage(::Image(rImage));
+ }
};
class SalInstanceCalendar : public SalInstanceWidget, public virtual weld::Calendar
@@ -3330,6 +3366,8 @@ public:
pHeaderBar->SetEndDragHdl(LINK(this, SalInstanceTreeView, EndDragHdl));
pHeaderBar->SetSelectHdl(LINK(this, SalInstanceTreeView, HeaderBarClickedHdl));
}
+ pHeaderBox->SetEditingEntryHdl(LINK(this, SalInstanceTreeView, EditingEntryHdl));
+ pHeaderBox->SetEditedEntryHdl(LINK(this, SalInstanceTreeView, EditedEntryHdl));
}
else
{
@@ -3884,16 +3922,11 @@ public:
return ::get_text_emphasis(pEntry, col);
}
- virtual void connect_editing_started(const Link<const weld::TreeIter&, bool>& rLink) override
+ virtual void connect_editing(const Link<const weld::TreeIter&, bool>& rStartLink,
+ const Link<const std::pair<const weld::TreeIter&, OUString>&, bool>& rEndLink) override
{
- m_xTreeView->EnableInplaceEditing(true);
- weld::TreeView::connect_editing_started(rLink);
- }
-
- virtual void connect_editing_done(const Link<const std::pair<const weld::TreeIter&, OUString>&, bool>& rLink) override
- {
- m_xTreeView->EnableInplaceEditing(true);
- weld::TreeView::connect_editing_done(rLink);
+ m_xTreeView->EnableInplaceEditing(rStartLink.IsSet() || rEndLink.IsSet());
+ weld::TreeView::connect_editing(rStartLink, rEndLink);
}
virtual void start_editing(const weld::TreeIter& rIter) override
@@ -4633,6 +4666,242 @@ IMPL_LINK(SalInstanceTreeView, EditedEntryHdl, IterString, rIterString, bool)
return signal_editing_done(std::pair<const weld::TreeIter&, OUString>(SalInstanceTreeIter(rIterString.first), rIterString.second));
}
+class SalInstanceIconView : public SalInstanceContainer, public virtual weld::IconView
+{
+private:
+ // owner for UserData
+ std::vector<std::unique_ptr<OUString>> m_aUserData;
+ VclPtr<::IconView> m_xIconView;
+
+ DECL_LINK(SelectHdl, SvTreeListBox*, void);
+ DECL_LINK(DeSelectHdl, SvTreeListBox*, void);
+ DECL_LINK(DoubleClickHdl, SvTreeListBox*, bool);
+
+public:
+ SalInstanceIconView(::IconView* pIconView, SalInstanceBuilder* pBuilder, bool bTakeOwnership)
+ : SalInstanceContainer(pIconView, pBuilder, bTakeOwnership)
+ , m_xIconView(pIconView)
+ {
+ m_xIconView->SetSelectHdl(LINK(this, SalInstanceIconView, SelectHdl));
+ m_xIconView->SetDeselectHdl(LINK(this, SalInstanceIconView, DeSelectHdl));
+ m_xIconView->SetDoubleClickHdl(LINK(this, SalInstanceIconView, DoubleClickHdl));
+ }
+
+ virtual void freeze() override
+ {
+ SalInstanceWidget::freeze();
+ m_xIconView->SetUpdateMode(false);
+ }
+
+ virtual void thaw() override
+ {
+ m_xIconView->SetUpdateMode(true);
+ SalInstanceWidget::thaw();
+ }
+
+ virtual void insert(int pos, const OUString* pStr, const OUString* pId,
+ const OUString* pIconName, weld::TreeIter* pRet) override
+ {
+ disable_notify_events();
+ auto nInsertPos = pos == -1 ? TREELIST_APPEND : pos;
+ void* pUserData;
+ if (pId)
+ {
+ m_aUserData.emplace_back(std::make_unique<OUString>(*pId));
+ pUserData = m_aUserData.back().get();
+ }
+ else
+ pUserData = nullptr;
+
+ SvTreeListEntry* pEntry = new SvTreeListEntry;
+ if (pIconName)
+ {
+ Image aImage(createImage(*pIconName));
+ pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aImage, aImage, false));
+ }
+ else
+ {
+ Image aDummy;
+ pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aDummy, aDummy, false));
+ }
+ if (pStr)
+ pEntry->AddItem(std::make_unique<SvLBoxString>(*pStr));
+ pEntry->SetUserData(pUserData);
+ m_xIconView->Insert(pEntry, nullptr, nInsertPos);
+
+ if (pRet)
+ {
+ SalInstanceTreeIter* pVclRetIter = static_cast<SalInstanceTreeIter*>(pRet);
+ pVclRetIter->iter = pEntry;
+ }
+
+ enable_notify_events();
+ }
+
+ virtual OUString get_selected_id() const override
+ {
+ assert(m_xIconView->IsUpdateMode() && "don't request selection when frozen");
+ if (SvTreeListEntry* pEntry = m_xIconView->FirstSelected())
+ {
+ if (const OUString* pStr = static_cast<const OUString*>(pEntry->GetUserData()))
+ return *pStr;
+ }
+ return OUString();
+ }
+
+ virtual OUString get_selected_text() const override
+ {
+ assert(m_xIconView->IsUpdateMode() && "don't request selection when frozen");
+ if (SvTreeListEntry* pEntry = m_xIconView->FirstSelected())
+ return m_xIconView->GetEntryText(pEntry);
+ return OUString();
+ }
+
+ virtual int count_selected_items() const override
+ {
+ return m_xIconView->GetSelectionCount();
+ }
+
+ virtual void select(int pos) override
+ {
+ assert(m_xIconView->IsUpdateMode() && "don't select when frozen");
+ disable_notify_events();
+ if (pos == -1 || (pos == 0 && n_children() == 0))
+ m_xIconView->SelectAll(false);
+ else
+ {
+ SvTreeListEntry* pEntry = m_xIconView->GetEntry(nullptr, pos);
+ m_xIconView->Select(pEntry, true);
+ m_xIconView->MakeVisible(pEntry);
+ }
+ enable_notify_events();
+ }
+
+ virtual void unselect(int pos) override
+ {
+ assert(m_xIconView->IsUpdateMode() && "don't select when frozen");
+ disable_notify_events();
+ if (pos == -1)
+ m_xIconView->SelectAll(true);
+ else
+ {
+ SvTreeListEntry* pEntry = m_xIconView->GetEntry(nullptr, pos);
+ m_xIconView->Select(pEntry, false);
+ }
+ enable_notify_events();
+ }
+
+ virtual int n_children() const override
+ {
+ return m_xIconView->GetModel()->GetChildList(nullptr).size();
+ }
+
+ virtual std::unique_ptr<weld::TreeIter> make_iterator(const weld::TreeIter* pOrig) const override
+ {
+ return std::unique_ptr<weld::TreeIter>(new SalInstanceTreeIter(static_cast<const SalInstanceTreeIter*>(pOrig)));
+ }
+
+ virtual bool get_selected(weld::TreeIter* pIter) const override
+ {
+ SvTreeListEntry* pEntry = m_xIconView->FirstSelected();
+ auto pVclIter = static_cast<SalInstanceTreeIter*>(pIter);
+ if (pVclIter)
+ pVclIter->iter = pEntry;
+ return pEntry != nullptr;
+ }
+
+ virtual bool get_cursor(weld::TreeIter* pIter) const override
+ {
+ SvTreeListEntry* pEntry = m_xIconView->GetCurEntry();
+ auto pVclIter = static_cast<SalInstanceTreeIter*>(pIter);
+ if (pVclIter)
+ pVclIter->iter = pEntry;
+ return pEntry != nullptr;
+ }
+
+ virtual void set_cursor(const weld::TreeIter& rIter) override
+ {
+ const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter);
+ disable_notify_events();
+ m_xIconView->SetCurEntry(rVclIter.iter);
+ enable_notify_events();
+ }
+
+ virtual bool get_iter_first(weld::TreeIter& rIter) const override
+ {
+ SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter);
+ rVclIter.iter = m_xIconView->GetEntry(0);
+ return rVclIter.iter != nullptr;
+ }
+
+ virtual void scroll_to_item(const weld::TreeIter& rIter) override
+ {
+ assert(m_xIconView->IsUpdateMode() && "don't select when frozen");
+ disable_notify_events();
+ const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter);
+ m_xIconView->MakeVisible(rVclIter.iter);
+ enable_notify_events();
+ }
+
+ virtual void selected_foreach(const std::function<bool(weld::TreeIter&)>& func) override
+ {
+ SalInstanceTreeIter aVclIter(m_xIconView->FirstSelected());
+ while (aVclIter.iter)
+ {
+ if (func(aVclIter))
+ return;
+ aVclIter.iter = m_xIconView->NextSelected(aVclIter.iter);
+ }
+ }
+
+ virtual OUString get_id(const weld::TreeIter& rIter) const override
+ {
+ const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter);
+ const OUString* pStr = static_cast<const OUString*>(rVclIter.iter->GetUserData());
+ if (pStr)
+ return *pStr;
+ return OUString();
+ }
+
+ virtual void clear() override
+ {
+ disable_notify_events();
+ m_xIconView->Clear();
+ m_aUserData.clear();
+ enable_notify_events();
+ }
+
+ virtual ~SalInstanceIconView() override
+ {
+ m_xIconView->SetDoubleClickHdl(Link<SvTreeListBox*, bool>());
+ m_xIconView->SetSelectHdl(Link<SvTreeListBox*, void>());
+ m_xIconView->SetDeselectHdl(Link<SvTreeListBox*, void>());
+ }
+};
+
+IMPL_LINK_NOARG(SalInstanceIconView, SelectHdl, SvTreeListBox*, void)
+{
+ if (notify_events_disabled())
+ return;
+ signal_selection_changed();
+}
+
+IMPL_LINK_NOARG(SalInstanceIconView, DeSelectHdl, SvTreeListBox*, void)
+{
+ if (notify_events_disabled())
+ return;
+ if (m_xIconView->GetSelectionMode() == SelectionMode::Single)
+ return;
+ signal_selection_changed();
+}
+
+IMPL_LINK_NOARG(SalInstanceIconView, DoubleClickHdl, SvTreeListBox*, bool)
+{
+ if (notify_events_disabled())
+ return false;
+ return !signal_item_activated();
+}
+
class SalInstanceSpinButton : public SalInstanceEntry, public virtual weld::SpinButton
{
private:
@@ -5472,7 +5741,7 @@ public:
}
}
- virtual void insert_separator(int pos) override
+ virtual void insert_separator(int pos, const OUString& /*rId*/) override
{
auto nInsertPos = pos == -1 ? m_xComboBox->GetEntryCount() : pos;
m_xComboBox->AddSeparator(nInsertPos - 1);
@@ -5588,7 +5857,7 @@ public:
}
}
- virtual void insert_separator(int pos) override
+ virtual void insert_separator(int pos, const OUString& /*rId*/) override
{
auto nInsertPos = pos == -1 ? m_xComboBox->GetEntryCount() : pos;
m_xComboBox->AddSeparator(nInsertPos - 1);
@@ -5667,7 +5936,7 @@ public:
rEntry.AddEventListener(LINK(this, SalInstanceEntryTreeView, KeyPressListener));
}
- virtual void insert_separator(int /*pos*/) override
+ virtual void insert_separator(int /*pos*/, const OUString& /*rId*/) override
{
assert(false);
}
@@ -6036,6 +6305,12 @@ public:
return pTreeView ? std::make_unique<SalInstanceTreeView>(pTreeView, this, bTakeOwnership) : nullptr;
}
+ virtual std::unique_ptr<weld::IconView> weld_icon_view(const OString &id, bool bTakeOwnership) override
+ {
+ IconView* pIconView = m_xBuilder->get<IconView>(id);
+ return pIconView ? std::make_unique<SalInstanceIconView>(pIconView, this, bTakeOwnership) : nullptr;
+ }
+
virtual std::unique_ptr<weld::Label> weld_label(const OString &id, bool bTakeOwnership) override
{
Control* pLabel = m_xBuilder->get<Control>(id);
diff --git a/vcl/source/treelist/iconview.cxx b/vcl/source/treelist/iconview.cxx
new file mode 100644
index 000000000000..5c2e5b2ffed5
--- /dev/null
+++ b/vcl/source/treelist/iconview.cxx
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/treelistentry.hxx>
+#include <vcl/viewdataentry.hxx>
+#include <iconview.hxx>
+#include <iconviewimpl.hxx>
+
+IconView::IconView( vcl::Window* pParent, WinBits nBits )
+ : SvTreeListBox( pParent, nBits )
+{
+ nColumns = 1;
+ mbCenterAndClipText = true;
+ SetEntryHeight( 100 );
+ SetEntryWidth( 100 );
+
+ pImpl.reset( new IconViewImpl( this, GetModel(), GetStyle() ) );
+}
+
+void IconView::Resize()
+{
+ Size aBoxSize = Control::GetParent()->GetOutputSizePixel();
+
+ if ( !aBoxSize.Width() )
+ return;
+
+ SetSizePixel( aBoxSize );
+
+ nColumns = aBoxSize.Width() / nEntryWidth;
+
+ SvTreeListBox::Resize();
+}
+
+tools::Rectangle IconView::GetFocusRect( SvTreeListEntry*, long nEntryPos )
+{
+ Size aSize;
+ aSize.setHeight( nEntryHeight );
+ aSize.setWidth( nEntryWidth );
+
+ Point aPos;
+ aPos.setX( 0 );
+ aPos.setY( 0 );
+
+ tools::Rectangle aRect;
+
+ short nCols = GetColumnsCount();
+
+ if(nCols)
+ {
+ aPos.setY( ( nEntryPos / nCols ) * nEntryHeight );
+ aPos.setX( ( nEntryPos % nCols ) * nEntryWidth );
+ }
+
+ aRect.SetPos( aPos );
+ aRect.SetSize( aSize );
+
+ return aRect;
+}
+
+void IconView::PaintEntry(SvTreeListEntry& rEntry, long nX, long nY,
+ vcl::RenderContext& rRenderContext)
+{
+
+ tools::Rectangle aRect; // multi purpose
+
+ PreparePaint(rRenderContext, rEntry);
+
+ pImpl->UpdateContextBmpWidthMax(&rEntry);
+
+ short nTempEntryHeight = GetEntryHeight();
+ short nTempEntryWidth = GetEntryWidth();
+
+ Point aEntryPos;
+
+ Color aBackupTextColor(rRenderContext.GetTextColor());
+ vcl::Font aBackupFont(rRenderContext.GetFont());
+ Color aBackupColor = rRenderContext.GetFillColor();
+
+ bool bCurFontIsSel = false;
+ const WinBits nWindowStyle = GetStyle();
+ const bool bHideSelection = (nWindowStyle & WB_HIDESELECTION) !=0 && !HasFocus();
+ const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ vcl::Font aHighlightFont(rRenderContext.GetFont());
+ const Color aHighlightTextColor(rSettings.GetHighlightTextColor());
+ aHighlightFont.SetColor(aHighlightTextColor);
+
+ Size aRectSize(nTempEntryWidth, nTempEntryHeight);
+
+ SvViewDataEntry* pViewDataEntry = GetViewDataEntry( &rEntry );
+
+ sal_uInt16 nItemCount = rEntry.ItemCount();
+ sal_uInt16 nCurItem = 0;
+ sal_uInt16 nIconItem = nItemCount;
+
+ while (nCurItem < nItemCount)
+ {
+ SvLBoxItem* pItem = nCurItem < nItemCount ? &rEntry.GetItem(nCurItem) : nullptr;
+ SvLBoxItemType nItemType = pItem->GetType();
+
+ if (nItemType == SvLBoxItemType::ContextBmp)
+ {
+ nIconItem = nCurItem;
+ nCurItem++;
+ continue;
+ }
+
+ auto nItemHeight = SvLBoxItem::GetHeight(pViewDataEntry, nCurItem);
+
+ aEntryPos.setX( nX );
+ aEntryPos.setY( nY );
+
+ // set background pattern/color
+
+ Wallpaper aWallpaper = rRenderContext.GetBackground();
+
+ if (pViewDataEntry->IsHighlighted())
+ {
+ Color aNewWallColor = rSettings.GetHighlightColor();
+ // if the face color is bright then the deactivate color is also bright
+ // -> so you can't see any deactivate selection
+ if (bHideSelection && !rSettings.GetFaceColor().IsBright()
+ && aWallpaper.GetColor().IsBright() != rSettings.GetDeactiveColor().IsBright())
+ {
+ aNewWallColor = rSettings.GetDeactiveColor();
+ }
+ // set font color to highlight
+ if (!bCurFontIsSel)
+ {
+ rRenderContext.SetTextColor(aHighlightTextColor);
+ rRenderContext.SetFont(aHighlightFont);
+ bCurFontIsSel = true;
+ }
+ aWallpaper.SetColor(aNewWallColor);
+ }
+ else // no selection
+ {
+ if (bCurFontIsSel)
+ {
+ bCurFontIsSel = false;
+ rRenderContext.SetTextColor(aBackupTextColor);
+ rRenderContext.SetFont(aBackupFont);
+ }
+ else
+ {
+ aWallpaper.SetColor(rEntry.GetBackColor());
+ }
+ }
+
+ // draw background
+ if (!(nTreeFlags & SvTreeFlags::USESEL))
+ {
+ aRect.SetPos(aEntryPos);
+ aRect.SetSize(aRectSize);
+
+ Color aBackgroundColor = aWallpaper.GetColor();
+ if (aBackgroundColor != COL_TRANSPARENT)
+ {
+ rRenderContext.SetFillColor(aBackgroundColor);
+ // this case may occur for smaller horizontal resizes
+ if (aRect.Left() < aRect.Right())
+ rRenderContext.DrawRect(aRect);
+ }
+ }
+
+ // center vertically
+ aEntryPos.AdjustY((nTempEntryHeight - nItemHeight) / 2 );
+
+ // draw item
+ pViewDataEntry->SetPaintRectangle(aRect);
+
+ aEntryPos.AdjustY(15 );
+
+ pItem->Paint(aEntryPos, *this, rRenderContext, pViewDataEntry, rEntry);
+
+ rRenderContext.SetFillColor(aBackupColor);
+
+ nCurItem++;
+ }
+
+ // draw icon
+ if(nIconItem != nItemCount && nIconItem < nItemCount)
+ {
+ SvLBoxItem* pItem = &rEntry.GetItem(nIconItem);
+ auto nItemWidth = pItem->GetWidth(this, pViewDataEntry, nIconItem);
+ auto nItemHeight = SvLBoxItem::GetHeight(pViewDataEntry, nIconItem);
+
+ aEntryPos.setX( nX );
+ aEntryPos.setY( nY );
+
+ // center horizontally
+ aEntryPos.AdjustX((nTempEntryWidth - nItemWidth) / 2 );
+ // center vertically
+ aEntryPos.AdjustY((nTempEntryHeight - nItemHeight) / 2 );
+
+ aEntryPos.AdjustY( -10 );
+
+ pItem->Paint(aEntryPos, *this, rRenderContext, pViewDataEntry, rEntry);
+ }
+
+ if (bCurFontIsSel)
+ {
+ rRenderContext.SetTextColor(aBackupTextColor);
+ rRenderContext.SetFont(aBackupFont);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/treelist/iconviewimpl.cxx b/vcl/source/treelist/iconviewimpl.cxx
new file mode 100644
index 000000000000..9014220d4820
--- /dev/null
+++ b/vcl/source/treelist/iconviewimpl.cxx
@@ -0,0 +1,662 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <tools/debug.hxx>
+#include <iconview.hxx>
+#include <iconviewimpl.hxx>
+
+IconViewImpl::IconViewImpl( SvTreeListBox* pTreeListBox, SvTreeList* pTreeList, WinBits nWinStyle )
+: SvImpLBox( pTreeListBox, pTreeList, nWinStyle )
+{
+}
+
+void IconViewImpl::CursorUp()
+{
+ if (!m_pStartEntry)
+ return;
+
+ SvTreeListEntry* pPrevFirstToDraw = m_pStartEntry;
+
+ for(short i = 0; i < m_pView->GetColumnsCount() && pPrevFirstToDraw; i++)
+ pPrevFirstToDraw = m_pView->PrevVisible(pPrevFirstToDraw);
+
+ if( !pPrevFirstToDraw )
+ return;
+
+ m_nFlags &= ~LBoxFlags::Filling;
+ long nEntryHeight = m_pView->GetEntryHeight();
+ ShowCursor( false );
+ m_pView->Update();
+ m_pStartEntry = pPrevFirstToDraw;
+ tools::Rectangle aArea( GetVisibleArea() );
+ aArea.AdjustBottom( -nEntryHeight );
+ m_pView->Scroll( 0, nEntryHeight, aArea, ScrollFlags::NoChildren );
+ m_pView->Update();
+ ShowCursor( true );
+ m_pView->NotifyScrolled();
+}
+
+void IconViewImpl::CursorDown()
+{
+ if (!m_pStartEntry)
+ return;
+
+ SvTreeListEntry* pNextFirstToDraw = m_pStartEntry;
+
+ for(short i = 0; i < m_pView->GetColumnsCount(); i++)
+ pNextFirstToDraw = m_pView->NextVisible(pNextFirstToDraw);
+
+ if( pNextFirstToDraw )
+ {
+ m_nFlags &= ~LBoxFlags::Filling;
+ ShowCursor( false );
+ m_pView->Update();
+ m_pStartEntry = pNextFirstToDraw;
+ tools::Rectangle aArea( GetVisibleArea() );
+ m_pView->Scroll( 0, -(m_pView->GetEntryHeight()), aArea, ScrollFlags::NoChildren );
+ m_pView->Update();
+ ShowCursor( true );
+ m_pView->NotifyScrolled();
+ }
+}
+
+void IconViewImpl::PageDown( sal_uInt16 nDelta )
+{
+ sal_uInt16 nRealDelta = nDelta * m_pView->GetColumnsCount();
+
+ if( !nDelta )
+ return;
+
+ if (!m_pStartEntry)
+ return;
+
+ SvTreeListEntry* pNext = m_pView->NextVisible(m_pStartEntry, nRealDelta);
+ if( pNext == m_pStartEntry )
+ return;
+
+ ShowCursor( false );
+
+ m_nFlags &= ~LBoxFlags::Filling;
+ m_pView->Update();
+ m_pStartEntry = pNext;
+
+ if( nRealDelta >= m_nVisibleCount )
+ {
+ m_pView->Invalidate( GetVisibleArea() );
+ m_pView->Update();
+ }
+ else
+ {
+ tools::Rectangle aArea( GetVisibleArea() );
+ long nScroll = m_pView->GetEntryHeight() * static_cast<long>(nRealDelta);
+ nScroll = -nScroll;
+ m_pView->Update();
+ m_pView->Scroll( 0, nScroll, aArea, ScrollFlags::NoChildren );
+ m_pView->Update();
+ m_pView->NotifyScrolled();
+ }
+
+ ShowCursor( true );
+}
+
+void IconViewImpl::PageUp( sal_uInt16 nDelta )
+{
+ sal_uInt16 nRealDelta = nDelta * m_pView->GetColumnsCount();
+ if( !nDelta )
+ return;
+
+ if (!m_pStartEntry)
+ return;
+
+ SvTreeListEntry* pPrev = m_pView->PrevVisible(m_pStartEntry, nRealDelta);
+ if( pPrev == m_pStartEntry )
+ return;
+
+ m_nFlags &= ~LBoxFlags::Filling;
+ ShowCursor( false );
+
+ m_pView->Update();
+ m_pStartEntry = pPrev;
+ if( nRealDelta >= m_nVisibleCount )
+ {
+ m_pView->Invalidate( GetVisibleArea() );
+ m_pView->Update();
+ }
+ else
+ {
+ long nEntryHeight = m_pView->GetEntryHeight();
+ tools::Rectangle aArea( GetVisibleArea() );
+ m_pView->Update();
+ m_pView->Scroll( 0, nEntryHeight*nRealDelta, aArea, ScrollFlags::NoChildren );
+ m_pView->Update();
+ m_pView->NotifyScrolled();
+ }
+
+ ShowCursor( true );
+}
+
+void IconViewImpl::KeyDown( bool bPageDown )
+{
+ if( !m_aVerSBar->IsVisible() )
+ return;
+
+ long nDelta;
+ if( bPageDown )
+ nDelta = m_aVerSBar->GetPageSize();
+ else
+ nDelta = 1;
+
+ long nThumbPos = m_aVerSBar->GetThumbPos();
+
+ if( nDelta <= 0 )
+ return;
+
+ m_nFlags &= ~LBoxFlags::Filling;
+ BeginScroll();
+
+ m_aVerSBar->SetThumbPos( nThumbPos+nDelta );
+ if( bPageDown )
+ PageDown( static_cast<short>(nDelta) );
+ else
+ CursorDown();
+
+ EndScroll();
+}
+
+void IconViewImpl::KeyUp( bool bPageUp )
+{
+ if( !m_aVerSBar->IsVisible() )
+ return;
+
+ long nDelta;
+ if( bPageUp )
+ nDelta = m_aVerSBar->GetPageSize();
+ else
+ nDelta = 1;
+
+ long nThumbPos = m_aVerSBar->GetThumbPos();
+
+ if( nThumbPos < nDelta )
+ nDelta = nThumbPos;
+
+ if( nDelta < 0 )
+ return;
+
+ m_nFlags &= ~LBoxFlags::Filling;
+ BeginScroll();
+
+ m_aVerSBar->SetThumbPos( nThumbPos - nDelta );
+ if( bPageUp )
+ PageUp( static_cast<short>(nDelta) );
+ else
+ CursorUp();
+
+ EndScroll();
+}
+
+long IconViewImpl::GetEntryLine( SvTreeListEntry* pEntry ) const
+{
+ if(!m_pStartEntry )
+ return -1; // invisible position
+
+ long nFirstVisPos = m_pView->GetVisiblePos( m_pStartEntry );
+ long nEntryVisPos = m_pView->GetVisiblePos( pEntry );
+ nFirstVisPos = nEntryVisPos - nFirstVisPos;
+
+ return nFirstVisPos;
+}
+
+Point IconViewImpl::GetEntryPosition( SvTreeListEntry* pEntry ) const
+{
+ const int pos = m_pView->GetAbsPos( pEntry );
+
+ return Point( ( pos % m_pView->GetColumnsCount() ) * m_pView->GetEntryWidth(),
+ ( pos / m_pView->GetColumnsCount() ) * m_pView->GetEntryHeight() );
+}
+
+SvTreeListEntry* IconViewImpl::GetClickedEntry( const Point& rPoint ) const
+{
+ DBG_ASSERT( m_pView->GetModel(), "IconViewImpl::GetClickedEntry: how can this ever happen?" );
+ if ( !m_pView->GetModel() )
+ return nullptr;
+ if( m_pView->GetEntryCount() == 0 || !m_pStartEntry || !m_pView->GetEntryHeight() || !m_pView->GetEntryWidth())
+ return nullptr;
+
+ sal_uInt16 nY = static_cast<sal_uInt16>(rPoint.Y() / m_pView->GetEntryHeight() );
+ sal_uInt16 nX = static_cast<sal_uInt16>(rPoint.X() / m_pView->GetEntryWidth() );
+ sal_uInt16 nTemp = nY * m_pView->GetColumnsCount() + nX;
+
+ SvTreeListEntry* pEntry = m_pView->NextVisible(m_pStartEntry, nTemp);
+
+ return pEntry;
+}
+
+bool IconViewImpl::IsEntryInView( SvTreeListEntry* pEntry ) const
+{
+ // parent collapsed
+ if( !m_pView->IsEntryVisible(pEntry) )
+ return false;
+
+ long nY = GetEntryLine( pEntry ) / m_pView->GetColumnsCount() * m_pView->GetEntryHeight();
+ if( nY < 0 )
+ return false;
+
+ long nMax = m_nVisibleCount / m_pView->GetColumnsCount() * m_pView->GetEntryHeight();
+ if( nY >= nMax )
+ return false;
+
+ long nStart = GetEntryLine( pEntry ) - GetEntryLine( m_pStartEntry );
+ return nStart >= 0;
+}
+
+void IconViewImpl::AdjustScrollBars( Size& rSize )
+{
+ long nEntryHeight = m_pView->GetEntryHeight();
+ if( !nEntryHeight )
+ return;
+
+ sal_uInt16 nResult = 0;
+
+ Size aOSize( m_pView->Control::GetOutputSizePixel() );
+
+ const WinBits nWindowStyle = m_pView->GetStyle();
+ bool bVerSBar = ( nWindowStyle & WB_VSCROLL ) != 0;
+
+ // number of entries that are not collapsed
+ sal_uLong nTotalCount = m_pView->GetVisibleCount();
+
+ // number of entries visible within the view
+ m_nVisibleCount = aOSize.Height() / nEntryHeight * m_pView->GetColumnsCount();
+
+ long nRows = ( nTotalCount / m_pView->GetColumnsCount() ) + 1;
+
+ // do we need a vertical scrollbar?
+ if( bVerSBar || nTotalCount > m_nVisibleCount )
+ {
+ nResult = 1;
+ }
+
+ PositionScrollBars( aOSize, nResult );
+
+ // adapt Range, VisibleRange etc.
+
+ // refresh output size, in case we have to scroll
+ tools::Rectangle aRect;
+ aRect.SetSize( aOSize );
+ m_aSelEng.SetVisibleArea( aRect );
+
+ // vertical scrollbar
+ if( !m_bInVScrollHdl )
+ {
+ m_aVerSBar->SetPageSize( nTotalCount );
+ m_aVerSBar->SetVisibleSize( nTotalCount - nRows );
+ }
+ else
+ {
+ m_nFlags |= LBoxFlags::EndScrollSetVisSize;
+ }
+
+ if( nResult & 0x0001 )
+ m_aVerSBar->Show();
+ else
+ m_aVerSBar->Hide();
+
+ rSize = aOSize;
+}
+
+// returns 0 if position is just past the last entry
+SvTreeListEntry* IconViewImpl::GetEntry( const Point& rPoint ) const
+{
+ if( (m_pView->GetEntryCount() == 0) || !m_pStartEntry ||
+ (rPoint.Y() > m_aOutputSize.Height())
+ || !m_pView->GetEntryHeight()
+ || !m_pView->GetEntryWidth())
+ return nullptr;
+
+ sal_uInt16 nClickedEntry = static_cast<sal_uInt16>(rPoint.Y() / m_pView->GetEntryHeight() * m_pView->GetColumnsCount() + rPoint.X() / m_pView->GetEntryWidth() );
+ sal_uInt16 nTemp = nClickedEntry;
+ SvTreeListEntry* pEntry = m_pView->NextVisible(m_pStartEntry, nTemp);
+ if( nTemp != nClickedEntry )
+ pEntry = nullptr;
+ return pEntry;
+}
+
+void IconViewImpl::SyncVerThumb()
+{
+ if( m_pStartEntry )
+ {
+ long nEntryPos = m_pView->GetVisiblePos( m_pStartEntry );
+ m_aVerSBar->SetThumbPos( nEntryPos );
+ }
+ else
+ m_aVerSBar->SetThumbPos( 0 );
+}
+
+void IconViewImpl::UpdateAll( bool bInvalidateCompleteView )
+{
+ FindMostRight( nullptr );
+ m_aVerSBar->SetRange( Range( 0, m_pView->GetVisibleCount() ) );
+ SyncVerThumb();
+ FillView();
+ ShowVerSBar();
+ if( m_bSimpleTravel && m_pCursor && m_pView->HasFocus() )
+ m_pView->Select( m_pCursor );
+ ShowCursor( true );
+ if( bInvalidateCompleteView )
+ m_pView->Invalidate();
+ else
+ m_pView->Invalidate( GetVisibleArea() );
+}
+
+void IconViewImpl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ if (!m_pView->GetVisibleCount())
+ return;
+
+ m_nFlags |= LBoxFlags::InPaint;
+
+ if (m_nFlags & LBoxFlags::Filling)
+ {
+ SvTreeListEntry* pFirst = m_pView->First();
+ if (pFirst != m_pStartEntry)
+ {
+ ShowCursor(false);
+ m_pStartEntry = m_pView->First();
+ m_aVerSBar->SetThumbPos( 0 );
+ StopUserEvent();
+ ShowCursor(true);
+ m_nCurUserEvent = Application::PostUserEvent(LINK(this, SvImpLBox, MyUserEvent),
+ reinterpret_cast<void*>(1));
+ return;
+ }
+ }
+
+ if (!m_pStartEntry)
+ {
+ m_pStartEntry = m_pView->First();
+ }
+
+ long nRectHeight = rRect.GetHeight();
+ long nRectWidth = rRect.GetWidth();
+ long nEntryHeight = m_pView->GetEntryHeight();
+ long nEntryWidth = m_pView->GetEntryWidth();
+
+ // calculate area for the entries we want to draw
+ sal_uInt16 nStartId = static_cast<sal_uInt16>(rRect.Top() / nEntryHeight * m_pView->GetColumnsCount() + (rRect.Left() / nEntryWidth));
+ sal_uInt16 nCount = static_cast<sal_uInt16>(( nRectHeight / nEntryHeight + 1 ) * nRectWidth / nEntryWidth);
+ nCount += 2; // don't miss an entry
+
+ long nY = nStartId / m_pView->GetColumnsCount() * nEntryHeight;
+ long nX = 0;
+ SvTreeListEntry* pEntry = m_pStartEntry;
+ while (nStartId && pEntry)
+ {
+ pEntry = m_pView->NextVisible(pEntry);
+ nStartId--;
+ }
+
+ vcl::Region aClipRegion(GetClipRegionRect());
+
+ if (!m_pCursor && !mbNoAutoCurEntry)
+ {
+ // do not select if multiselection or explicit set
+ bool bNotSelect = (m_aSelEng.GetSelectionMode() == SelectionMode::Multiple ) || ((m_nStyle & WB_NOINITIALSELECTION) == WB_NOINITIALSELECTION);
+ SetCursor(m_pStartEntry, bNotSelect);
+ }
+
+ for(sal_uInt16 n = 0; n< nCount && pEntry; n++)
+ {
+ static_cast<IconView*>(m_pView.get())->PaintEntry(*pEntry, nX, nY, rRenderContext);
+ nX += nEntryWidth;
+
+ if(nX + m_pView->GetEntryWidth() > nEntryWidth * m_pView->GetColumnsCount())
+ {
+ nY += nEntryHeight;
+ nX = 0;
+ }
+ pEntry = m_pView->NextVisible(pEntry);
+ }
+
+ m_nFlags &= ~LBoxFlags::DeselectAll;
+ rRenderContext.SetClipRegion();
+ m_nFlags &= ~LBoxFlags::InPaint;
+}
+
+void IconViewImpl::InvalidateEntry( long nId ) const
+{
+ if( m_nFlags & LBoxFlags::InPaint )
+ return;
+
+ tools::Rectangle aRect( GetVisibleArea() );
+ long nMaxBottom = aRect.Bottom();
+ aRect.SetTop( nId / m_pView->GetColumnsCount() * m_pView->GetEntryHeight() );
+ aRect.SetBottom( aRect.Top() ); aRect.AdjustBottom(m_pView->GetEntryHeight() );
+
+ if( aRect.Top() > nMaxBottom )
+ return;
+ if( aRect.Bottom() > nMaxBottom )
+ aRect.SetBottom( nMaxBottom );
+ m_pView->Invalidate( aRect );
+}
+
+bool IconViewImpl::KeyInput( const KeyEvent& rKEvt )
+{
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+
+ if( rKeyCode.IsMod2() )
+ return false; // don't evaluate Alt key
+
+ m_nFlags &= ~LBoxFlags::Filling;
+
+ if( !m_pCursor )
+ m_pCursor = m_pStartEntry;
+ if( !m_pCursor )
+ return false;
+
+ sal_uInt16 aCode = rKeyCode.GetCode();
+
+ bool bShift = rKeyCode.IsShift();
+ bool bMod1 = rKeyCode.IsMod1();
+
+ SvTreeListEntry* pNewCursor;
+
+ bool bHandled = true;
+
+ long i;
+ long nColumns = m_pView->GetColumnsCount();
+
+ switch( aCode )
+ {
+ case KEY_LEFT:
+ if( !IsEntryInView( m_pCursor ) )
+ MakeVisible( m_pCursor );
+
+ pNewCursor = m_pCursor;
+ do
+ {
+ pNewCursor = m_pView->PrevVisible(pNewCursor);
+ } while( pNewCursor && !IsSelectable(pNewCursor) );
+
+ // if there is no next entry, take the current one
+ // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
+ // the cursor key
+ if (!pNewCursor)
+ pNewCursor = m_pCursor;
+
+ m_aSelEng.CursorPosChanging( bShift, bMod1 );
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ if( !IsEntryInView( pNewCursor ) )
+ KeyUp( false );
+ break;
+
+ case KEY_RIGHT:
+ if( !IsEntryInView( m_pCursor ) )
+ MakeVisible( m_pCursor );
+
+ pNewCursor = m_pCursor;
+ do
+ {
+ pNewCursor = m_pView->NextVisible(pNewCursor);
+ } while( pNewCursor && !IsSelectable(pNewCursor) );
+
+ // if there is no next entry, take the current one
+ // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
+ // the cursor key
+ if ( !pNewCursor && m_pCursor )
+ pNewCursor = m_pCursor;
+
+ if( pNewCursor )
+ {
+ m_aSelEng.CursorPosChanging( bShift, bMod1 );
+ if( IsEntryInView( pNewCursor ) )
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ else
+ {
+ if( m_pCursor )
+ m_pView->Select( m_pCursor, false );
+ KeyDown( false );
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ }
+ }
+ else
+ KeyDown( false ); // because scrollbar range might still
+ // allow scrolling
+ break;
+
+ case KEY_UP:
+ {
+ if( !IsEntryInView( m_pCursor ) )
+ MakeVisible( m_pCursor );
+
+ pNewCursor = m_pCursor;
+ for( i = 0; i < nColumns && pNewCursor; i++)
+ {
+ do
+ {
+ pNewCursor = m_pView->PrevVisible(pNewCursor);
+ } while( pNewCursor && !IsSelectable(pNewCursor) );
+ }
+
+ // if there is no next entry, take the current one
+ // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
+ // the cursor key
+ if ( !pNewCursor && m_pCursor )
+ pNewCursor = m_pCursor;
+
+ if( pNewCursor )
+ {
+ m_aSelEng.CursorPosChanging( bShift, bMod1 );
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ if( !IsEntryInView( pNewCursor ) )
+ KeyUp( false );
+ }
+ break;
+ }
+
+ case KEY_DOWN:
+ {
+ if( !IsEntryInView( m_pCursor ) )
+ MakeVisible( m_pCursor );
+
+ pNewCursor = m_pCursor;
+ for( i = 0; i < nColumns && pNewCursor; i++)
+ {
+ do
+ {
+ pNewCursor = m_pView->NextVisible(pNewCursor);
+ } while( pNewCursor && !IsSelectable(pNewCursor) );
+ }
+
+ // if there is no next entry, take the current one
+ // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
+ // the cursor key
+ if ( !pNewCursor && m_pCursor )
+ pNewCursor = m_pCursor;
+
+ if( pNewCursor )
+ {
+ m_aSelEng.CursorPosChanging( bShift, bMod1 );
+ if( IsEntryInView( pNewCursor ) )
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ else
+ {
+ if( m_pCursor )
+ m_pView->Select( m_pCursor, false );
+ KeyDown( false );
+ SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
+ }
+ }
+ else
+ KeyDown( false ); // because scrollbar range might still
+ // allow scrolling
+ break;
+ }
+
+ case KEY_RETURN:
+ {
+ m_pView->aDoubleClickHdl.Call( m_pView );
+ bHandled = true;
+
+ break;
+ }
+
+ case KEY_END:
+ {
+ pNewCursor = m_pView->GetModel()->Last();
+
+ while( pNewCursor && !IsSelectable(pNewCursor) )
+ {
+ pNewCursor = m_pView->PrevVisible(pNewCursor);
+ }
+
+ m_pStartEntry = pNewCursor;
+
+ while( m_pStartEntry && m_pView->GetAbsPos( m_pStartEntry ) % m_pView->GetColumnsCount() != 0 )
+ {
+ m_pStartEntry = m_pView->PrevVisible(m_pStartEntry);
+ }
+
+ if( pNewCursor && pNewCursor != m_pCursor)
+ {
+// SelAllDestrAnch( false );
+ m_aSelEng.CursorPosChanging( bShift, bMod1 );
+ SetCursor( pNewCursor );
+ SyncVerThumb();
+ }
+
+ bHandled = true;
+
+ break;
+ }
+
+ default:
+ {
+ bHandled = false;
+ break;
+ }
+ }
+
+ if(!bHandled)
+ return SvImpLBox::KeyInput( rKEvt );
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx
index f6b38a510d5b..67f96160165b 100644
--- a/vcl/source/window/builder.cxx
+++ b/vcl/source/window/builder.cxx
@@ -48,6 +48,7 @@
#include <vcl/slider.hxx>
#include <vcl/weld.hxx>
#include <vcl/commandinfoprovider.hxx>
+#include <iconview.hxx>
#include <svdata.hxx>
#include <bitmaps.hlst>
#include <messagedialog.hxx>
@@ -2040,6 +2041,29 @@ VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OString &
xWindow = xBox;
}
}
+ else if (name == "GtkIconView")
+ {
+ assert(rMap.find(OString("model")) != rMap.end() && "GtkIconView must have a model");
+
+ //window we want to apply the packing props for this GtkIconView to
+ VclPtr<vcl::Window> xWindowForPackingProps;
+ extractModel(id, rMap);
+ WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK|WB_HIDESELECTION;
+ //IconView manages its own scrolling,
+ vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
+ if (pRealParent != pParent)
+ nWinStyle |= WB_BORDER;
+
+ VclPtr<IconView> xBox = VclPtr<IconView>::Create(pRealParent, nWinStyle);
+ xWindowForPackingProps = xBox;
+
+ xWindow = xBox;
+ xBox->SetNoAutoCurEntry(true);
+ xBox->SetQuickSearch(true);
+
+ if (pRealParent != pParent)
+ cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
+ }
else if (name == "GtkTreeView")
{
if (!m_bLegacy)
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index 9cc600dc12b5..b72495c13dc4 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -1907,6 +1907,21 @@ private:
bool signal_button(GdkEventButton* pEvent)
{
+ Point aPos(pEvent->x, pEvent->y);
+ if (SwapForRTL())
+ aPos.setX(gtk_widget_get_allocated_width(m_pWidget) - 1 - aPos.X());
+
+ if (gdk_event_triggers_context_menu(reinterpret_cast<GdkEvent*>(pEvent)) && pEvent->type == GDK_BUTTON_PRESS)
+ {
+ //if handled for context menu, stop processing
+ CommandEvent aCEvt(aPos, CommandEventId::ContextMenu, true);
+ if (signal_popup_menu(aCEvt))
+ return true;
+ }
+
+ if (!m_aMousePressHdl.IsSet() && !m_aMouseReleaseHdl.IsSet())
+ return false;
+
SalEvent nEventType = SalEvent::NONE;
switch (pEvent->type)
{
@@ -1954,19 +1969,6 @@ private:
return false;
}
- Point aPos(pEvent->x, pEvent->y);
-
- if (SwapForRTL())
- aPos.setX(gtk_widget_get_allocated_width(m_pWidget) - 1 - aPos.X());
-
- if (gdk_event_triggers_context_menu(reinterpret_cast<GdkEvent*>(pEvent)) && pEvent->type == GDK_BUTTON_PRESS)
- {
- //if handled for context menu, stop processing
- CommandEvent aCEvt(aPos, CommandEventId::ContextMenu, true);
- if (signal_popup_menu(aCEvt))
- return true;
- }
-
sal_uInt32 nModCode = GtkSalFrame::GetMouseModCode(pEvent->state);
sal_uInt16 nCode = m_nLastMouseButton | (nModCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2));
MouseEvent aMEvt(aPos, m_nLastMouseClicks, ImplGetMouseButtonMode(m_nLastMouseButton, nModCode), nCode, nCode);
@@ -3111,6 +3113,11 @@ public:
m_aMap.clear();
}
+ GtkMenu* getMenu() const
+ {
+ return m_pMenu;
+ }
+
virtual ~MenuHelper()
{
for (auto& a : m_aMap)
@@ -3305,12 +3312,13 @@ namespace
int getButtonPriority(const OString &rType)
{
- static const size_t N_TYPES = 6;
+ static const size_t N_TYPES = 7;
static const ButtonOrder aDiscardCancelSave[N_TYPES] =
{
{ "/discard", 0 },
{ "/cancel", 1 },
{ "/no", 2 },
+ { "/open", 3 },
{ "/save", 3 },
{ "/yes", 3 },
{ "/ok", 3 }
@@ -3318,6 +3326,7 @@ namespace
static const ButtonOrder aSaveDiscardCancel[N_TYPES] =
{
+ { "/open", 0 },
{ "/save", 0 },
{ "/yes", 0 },
{ "/ok", 0 },
@@ -6555,6 +6564,8 @@ public:
}
}
+ void set_menu(weld::Menu* pMenu);
+
virtual ~GtkInstanceMenuButton() override
{
if (m_pMenuHack)
@@ -6785,6 +6796,14 @@ public:
}
};
+void GtkInstanceMenuButton::set_menu(weld::Menu* pMenu)
+{
+ GtkInstanceMenu* pPopoverWidget = dynamic_cast<GtkInstanceMenu*>(pMenu);
+ m_pPopover = nullptr;
+ GtkWidget* pMenuWidget = GTK_WIDGET(pPopoverWidget ? pPopoverWidget->getMenu() : nullptr);
+ gtk_menu_button_set_popup(m_pMenuButton, pMenuWidget);
+}
+
class GtkInstanceToolbar : public GtkInstanceWidget, public virtual weld::Toolbar
{
private:
@@ -6916,6 +6935,11 @@ public:
m_aMenuButtonMap[rIdent]->set_popover(pPopover);
}
+ virtual void set_item_menu(const OString& rIdent, weld::Menu* pMenu) override
+ {
+ m_aMenuButtonMap[rIdent]->set_menu(pMenu);
+ }
+
virtual ~GtkInstanceToolbar() override
{
for (auto& a : m_aMap)
@@ -7136,13 +7160,26 @@ public:
virtual void set_image(VirtualDevice* pDevice) override
{
if (gtk_check_version(3, 20, 0) == nullptr)
- gtk_image_set_from_surface(m_pImage, get_underlying_cairo_surface(*pDevice));
- else
{
- GdkPixbuf* pixbuf = getPixbuf(*pDevice);
- gtk_image_set_from_pixbuf(m_pImage, pixbuf);
- g_object_unref(pixbuf);
+ if (pDevice)
+ gtk_image_set_from_surface(m_pImage, get_underlying_cairo_surface(*pDevice));
+ else
+ gtk_image_set_from_surface(m_pImage, nullptr);
+ return;
}
+
+ GdkPixbuf* pixbuf = pDevice ? getPixbuf(*pDevice) : nullptr;
+ gtk_image_set_from_pixbuf(m_pImage, pixbuf);
+ if (pixbuf)
+ g_object_unref(pixbuf);
+ }
+
+ virtual void set_image(const css::uno::Reference<css::graphic::XGraphic>& rImage) override
+ {
+ GdkPixbuf* pixbuf = getPixbuf(rImage);
+ gtk_image_set_from_pixbuf(m_pImage, pixbuf);
+ if (pixbuf)
+ g_object_unref(pixbuf);
}
};
@@ -8032,6 +8069,15 @@ private:
pThis->signal_cell_edited(pCell, path, pNewText);
}
+ static void restoreNonEditable(GObject* pCell)
+ {
+ if (g_object_get_data(pCell, "g-lo-RestoreNonEditable"))
+ {
+ g_object_set(pCell, "editable", false, "editable-set", false, nullptr);
+ g_object_set_data(pCell, "g-lo-RestoreNonEditable", reinterpret_cast<gpointer>(false));
+ }
+ }
+
void signal_cell_edited(GtkCellRendererText* pCell, const gchar *path, const gchar* pNewText)
{
GtkTreePath *tree_path = gtk_tree_path_new_from_string(path);
@@ -8047,6 +8093,13 @@ private:
void* pData = g_object_get_data(G_OBJECT(pCell), "g-lo-CellIndex");
set(aGtkIter.iter, reinterpret_cast<sal_IntPtr>(pData), sText);
}
+
+ restoreNonEditable(G_OBJECT(pCell));
+ }
+
+ static void signalCellEditingCanceled(GtkCellRenderer* pCell, gpointer /*widget*/)
+ {
+ restoreNonEditable(G_OBJECT(pCell));
}
void signal_column_clicked(GtkTreeViewColumn* pClickedColumn)
@@ -8202,6 +8255,7 @@ public:
m_aWeightMap[nIndex] = -1;
m_aSensitiveMap[nIndex] = -1;
g_signal_connect(G_OBJECT(pCellRenderer), "editing-started", G_CALLBACK(signalCellEditingStarted), this);
+ g_signal_connect(G_OBJECT(pCellRenderer), "editing-canceled", G_CALLBACK(signalCellEditingCanceled), this);
g_signal_connect(G_OBJECT(pCellRenderer), "edited", G_CALLBACK(signalCellEdited), this);
}
else if (GTK_IS_CELL_RENDERER_TOGGLE(pCellRenderer))
@@ -9228,7 +9282,6 @@ public:
if (!gtk_tree_view_row_expanded(m_pTreeView, path))
gtk_tree_view_expand_to_path(m_pTreeView, path);
gtk_tree_path_free(path);
-
}
virtual void collapse_row(const weld::TreeIter& rIter) override
@@ -9525,6 +9578,27 @@ public:
GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore);
GtkTreePath* path = gtk_tree_model_get_path(pModel, const_cast<GtkTreeIter*>(&rGtkIter.iter));
+ // allow editing of cells which are not usually editable, so we can have double click
+ // do its usual row-activate but if we explicitly want to edit (remote files dialog)
+ // we can still do that
+ GList *pRenderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(pColumn));
+ for (GList* pRenderer = g_list_first(pRenderers); pRenderer; pRenderer = g_list_next(pRenderer))
+ {
+ GtkCellRenderer* pCellRenderer = GTK_CELL_RENDERER(pRenderer->data);
+ if (GTK_IS_CELL_RENDERER_TEXT(pCellRenderer))
+ {
+ gboolean is_editable(false);
+ g_object_get(pCellRenderer, "editable", &is_editable, nullptr);
+ if (!is_editable)
+ {
+ g_object_set(pCellRenderer, "editable", true, "editable-set", true, nullptr);
+ g_object_set_data(G_OBJECT(pCellRenderer), "g-lo-RestoreNonEditable", reinterpret_cast<gpointer>(true));
+ break;
+ }
+ }
+ }
+ g_list_free(pRenderers);
+
gtk_tree_view_set_cursor(m_pTreeView, path, pColumn, true);
gtk_tree_path_free(path);
@@ -9614,6 +9688,373 @@ IMPL_LINK_NOARG(GtkInstanceTreeView, async_stop_cell_editing, void*, void)
end_editing();
}
+class GtkInstanceIconView : public GtkInstanceContainer, public virtual weld::IconView
+{
+private:
+ GtkIconView* m_pIconView;
+ GtkTreeStore* m_pTreeStore;
+ std::vector<int> m_aViewColToModelCol;
+ std::vector<int> m_aModelColToViewCol;
+ gint m_nTextCol;
+ gint m_nImageCol;
+ gint m_nIdCol;
+ gulong m_nSelectionChangedSignalId;
+ gulong m_nItemActivatedSignalId;
+ ImplSVEvent* m_pSelectionChangeEvent;
+
+ DECL_LINK(async_signal_selection_changed, void*, void);
+
+ void launch_signal_selection_changed()
+ {
+ //tdf#117991 selection change is sent before the focus change, and focus change
+ //is what will cause a spinbutton that currently has the focus to set its contents
+ //as the spin button value. So any LibreOffice callbacks on
+ //signal-change would happen before the spinbutton value-change occurs.
+ //To avoid this, send the signal-change to LibreOffice to occur after focus-change
+ //has been processed
+ if (m_pSelectionChangeEvent)
+ Application::RemoveUserEvent(m_pSelectionChangeEvent);
+ m_pSelectionChangeEvent = Application::PostUserEvent(LINK(this, GtkInstanceIconView, async_signal_selection_changed));
+ }
+
+ static void signalSelectionChanged(GtkIconView*, gpointer widget)
+ {
+ GtkInstanceIconView* pThis = static_cast<GtkInstanceIconView*>(widget);
+ pThis->launch_signal_selection_changed();
+ }
+
+ void handle_item_activated()
+ {
+ if (signal_item_activated())
+ return;
+ }
+
+ static void signalItemActivated(GtkIconView*, GtkTreePath*, gpointer widget)
+ {
+ GtkInstanceIconView* pThis = static_cast<GtkInstanceIconView*>(widget);
+ SolarMutexGuard aGuard;
+ pThis->handle_item_activated();
+ }
+
+ void insert_item(GtkTreeIter& iter, int pos, const OUString* pId, const OUString* pText, const OUString* pIconName)
+ {
+ gtk_tree_store_insert_with_values(m_pTreeStore, &iter, nullptr, pos,
+ m_nTextCol, !pText ? nullptr : OUStringToOString(*pText, RTL_TEXTENCODING_UTF8).getStr(),
+ m_nIdCol, !pId ? nullptr : OUStringToOString(*pId, RTL_TEXTENCODING_UTF8).getStr(),
+ -1);
+ if (pIconName)
+ {
+ GdkPixbuf* pixbuf = getPixbuf(*pIconName);
+ gtk_tree_store_set(m_pTreeStore, &iter, m_nImageCol, pixbuf, -1);
+ if (pixbuf)
+ g_object_unref(pixbuf);
+ }
+ }
+
+ OUString get(const GtkTreeIter& iter, int col) const
+ {
+ GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore);
+ gchar* pStr;
+ gtk_tree_model_get(pModel, const_cast<GtkTreeIter*>(&iter), col, &pStr, -1);
+ OUString sRet(pStr, pStr ? strlen(pStr) : 0, RTL_TEXTENCODING_UTF8);
+ g_free(pStr);
+ return sRet;
+ }
+
+ bool get_selected_iterator(GtkTreeIter* pIter) const
+ {
+ assert(gtk_icon_view_get_model(m_pIconView) && "don't request selection when frozen");
+ bool bRet = false;
+ {
+ GtkTreeModel* pModel = GTK_TREE_MODEL(m_pTreeStore);
+ GList* pList = gtk_icon_view_get_selected_items(m_pIconView);
+ for (GList* pItem = g_list_first(pList); pItem; pItem = g_list_next(pItem))
+ {
+ if (pIter)
+ {
+ GtkTreePath* path = static_cast<GtkTreePath*>(pItem->data);
+ gtk_tree_model_get_iter(pModel, pIter, path);
+ }
+ bRet = true;
+ break;
+ }
+ g_list_free_full(pList, reinterpret_cast<GDestroyNotify>(gtk_tree_path_free));
+ }
+ return bRet;
+ }
+
+public:
+ GtkInstanceIconView(GtkIconView* pIconView, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
+ : GtkInstanceContainer(GTK_CONTAINER(pIconView), pBuilder, bTakeOwnership)
+ , m_pIconView(pIconView)
+ , m_pTreeStore(GTK_TREE_STORE(gtk_icon_view_get_model(m_pIconView)))
+ , m_nTextCol(gtk_icon_view_get_text_column(m_pIconView))
+ , m_nImageCol(gtk_icon_view_get_pixbuf_column(m_pIconView))
+ , m_nSelectionChangedSignalId(g_signal_connect(pIconView, "selection-changed",
+ G_CALLBACK(signalSelectionChanged), this))
+ , m_nItemActivatedSignalId(g_signal_connect(pIconView, "item-activated", G_CALLBACK(signalItemActivated), this))
+ , m_pSelectionChangeEvent(nullptr)
+ {
+ m_nIdCol = m_nTextCol + 1;
+ }
+
+ virtual void insert(int pos, const OUString* pText, const OUString* pId, const OUString* pIconName, weld::TreeIter* pRet) override
+ {
+ disable_notify_events();
+ GtkTreeIter iter;
+ insert_item(iter, pos, pId, pText, pIconName);
+ if (pRet)
+ {
+ GtkInstanceTreeIter* pGtkRetIter = static_cast<GtkInstanceTreeIter*>(pRet);
+ pGtkRetIter->iter = iter;
+ }
+ enable_notify_events();
+ }
+
+ virtual OUString get_selected_id() const override
+ {
+ assert(gtk_icon_view_get_model(m_pIconView) && "don't request selection when frozen");
+ GtkTreeIter iter;
+ if (get_selected_iterator(&iter))
+ return get(iter, m_nIdCol);
+ return OUString();
+ }
+
+ virtual void clear() override
+ {
+ disable_notify_events();
+ gtk_tree_store_clear(m_pTreeStore);
+ enable_notify_events();
+ }
+
+ virtual void freeze() override
+ {
+ disable_notify_events();
+ g_object_ref(m_pTreeStore);
+ GtkInstanceContainer::freeze();
+ gtk_icon_view_set_model(m_pIconView, nullptr);
+ enable_notify_events();
+ }
+
+ virtual void thaw() override
+ {
+ disable_notify_events();
+ gtk_icon_view_set_model(m_pIconView, GTK_TREE_MODEL(m_pTreeStore));
+ GtkInstanceContainer::thaw();
+ g_object_unref(m_pTreeStore);
+ enable_notify_events();
+ }
+
+ virtual Size get_size_request() const override
+ {
+ GtkWidget* pParent = gtk_widget_get_parent(m_pWidget);
+ if (GTK_IS_SCROLLED_WINDOW(pParent))
+ {
+ return Size(gtk_scrolled_window_get_min_content_width(GTK_SCROLLED_WINDOW(pParent)),
+ gtk_scrolled_window_get_min_content_height(GTK_SCROLLED_WINDOW(pParent)));
+ }
+ int nWidth, nHeight;
+ gtk_widget_get_size_request(m_pWidget, &nWidth, &nHeight);
+ return Size(nWidth, nHeight);
+ }
+
+ virtual Size get_preferred_size() const override
+ {
+ Size aRet(-1, -1);
+ GtkWidget* pParent = gtk_widget_get_parent(m_pWidget);
+ if (GTK_IS_SCROLLED_WINDOW(pParent))
+ {
+ aRet = Size(gtk_scrolled_window_get_min_content_width(GTK_SCROLLED_WINDOW(pParent)),
+ gtk_scrolled_window_get_min_content_height(GTK_SCROLLED_WINDOW(pParent)));
+ }
+ GtkRequisition size;
+ gtk_widget_get_preferred_size(m_pWidget, nullptr, &size);
+ if (aRet.Width() == -1)
+ aRet.setWidth(size.width);
+ if (aRet.Height() == -1)
+ aRet.setHeight(size.height);
+ return aRet;
+ }
+
+ virtual void show() override
+ {
+ GtkWidget* pParent = gtk_widget_get_parent(m_pWidget);
+ if (GTK_IS_SCROLLED_WINDOW(pParent))
+ gtk_widget_show(pParent);
+ gtk_widget_show(m_pWidget);
+ }
+
+ virtual void hide() override
+ {
+ GtkWidget* pParent = gtk_widget_get_parent(m_pWidget);
+ if (GTK_IS_SCROLLED_WINDOW(pParent))
+ gtk_widget_hide(pParent);
+ gtk_widget_hide(m_pWidget);
+ }
+
+ virtual OUString get_selected_text() const override
+ {
+ assert(gtk_icon_view_get_model(m_pIconView) && "don't request selection when frozen");
+ GtkTreeIter iter;
+ if (get_selected_iterator(&iter))
+ return get(iter, m_nTextCol);
+ return OUString();
+ }
+
+ virtual int count_selected_items() const override
+ {
+ GList* pList = gtk_icon_view_get_selected_items(m_pIconView);
+ int nRet = g_list_length(pList);
+ g_list_free_full(pList, reinterpret_cast<GDestroyNotify>(gtk_tree_path_free));
+ return nRet;
+ }
+
+ virtual void select(int pos) override
+ {
+ assert(gtk_icon_view_get_model(m_pIconView) && "don't select when frozen");
+ disable_notify_events();
+ if (pos == -1 || (pos == 0 && n_children() == 0))
+ {
+ gtk_icon_view_unselect_all(m_pIconView);
+ }
+ else
+ {
+ GtkTreePath* path = gtk_tree_path_new_from_indices(pos, -1);
+ gtk_icon_view_select_path(m_pIconView, path);
+ gtk_icon_view_scroll_to_path(m_pIconView, path, false, 0, 0);
+ gtk_tree_path_free(path);
+ }
+ enable_notify_events();
+ }
+
+ virtual void unselect(int pos) override
+ {
+ assert(gtk_icon_view_get_model(m_pIconView) && "don't select when frozen");
+ disable_notify_events();
+ if (pos == -1 || (pos == 0 && n_children() == 0))
+ {
+ gtk_icon_view_select_all(m_pIconView);
+ }
+ else
+ {
+ GtkTreePath* path = gtk_tree_path_new_from_indices(pos, -1);
+ gtk_icon_view_select_path(m_pIconView, path);
+ gtk_tree_path_free(path);
+ }
+ enable_notify_events();
+ }
+
+ virtual bool get_selected(weld::TreeIter* pIter) const override
+ {
+ GtkInstanceTreeIter* pGtkIter = static_cast<GtkInstanceTreeIter*>(pIter);
+ return get_selected_iterator(pGtkIter ? &pGtkIter->iter : nullptr);
+ }
+
+ virtual bool get_cursor(weld::TreeIter* pIter) const override
+ {
+ GtkInstanceTreeIter* pGtkIter = static_cast<GtkInstanceTreeIter*>(pIter);
+ GtkTreePath* path;
+ gtk_icon_view_get_cursor(m_pIconView, &path, nullptr);
+ if (pGtkIter && path)
+ {
+ GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore);
+ gtk_tree_model_get_iter(pModel, &pGtkIter->iter, path);
+ }
+ return path != nullptr;
+ }
+
+ virtual void set_cursor(const weld::TreeIter& rIter) override
+ {
+ const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter);
+ GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore);
+ GtkTreePath* path = gtk_tree_model_get_path(pModel, const_cast<GtkTreeIter*>(&rGtkIter.iter));
+ gtk_icon_view_set_cursor(m_pIconView, path, nullptr, false);
+ gtk_tree_path_free(path);
+ }
+
+ virtual bool get_iter_first(weld::TreeIter& rIter) const override
+ {
+ GtkInstanceTreeIter& rGtkIter = static_cast<GtkInstanceTreeIter&>(rIter);
+ GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore);
+ return gtk_tree_model_get_iter_first(pModel, &rGtkIter.iter);
+ }
+
+ virtual void scroll_to_item(const weld::TreeIter& rIter) override
+ {
+ assert(gtk_icon_view_get_model(m_pIconView) && "don't select when frozen");
+ disable_notify_events();
+ const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter);
+ GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore);
+ GtkTreePath* path = gtk_tree_model_get_path(pModel, const_cast<GtkTreeIter*>(&rGtkIter.iter));
+ gtk_icon_view_scroll_to_path(m_pIconView, path, false, 0, 0);
+ gtk_tree_path_free(path);
+ enable_notify_events();
+ }
+
+ 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)));
+ }
+
+ virtual void selected_foreach(const std::function<bool(weld::TreeIter&)>& func) override
+ {
+ GtkInstanceTreeIter aGtkIter(nullptr);
+
+ GtkTreeModel *pModel = GTK_TREE_MODEL(m_pTreeStore);
+ GList* pList = gtk_icon_view_get_selected_items(m_pIconView);
+ for (GList* pItem = g_list_first(pList); pItem; pItem = g_list_next(pItem))
+ {
+ GtkTreePath* path = static_cast<GtkTreePath*>(pItem->data);
+ gtk_tree_model_get_iter(pModel, &aGtkIter.iter, path);
+ if (func(aGtkIter))
+ break;
+ }
+ g_list_free_full(pList, reinterpret_cast<GDestroyNotify>(gtk_tree_path_free));
+ }
+
+ virtual int n_children() const override
+ {
+ return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_pTreeStore), nullptr);
+ }
+
+ virtual OUString get_id(const weld::TreeIter& rIter) const override
+ {
+ const GtkInstanceTreeIter& rGtkIter = static_cast<const GtkInstanceTreeIter&>(rIter);
+ return get(rGtkIter.iter, m_nIdCol);
+ }
+
+ virtual void disable_notify_events() override
+ {
+ g_signal_handler_block(m_pIconView, m_nSelectionChangedSignalId);
+ g_signal_handler_block(m_pIconView, m_nItemActivatedSignalId);
+
+ GtkInstanceContainer::disable_notify_events();
+ }
+
+ virtual void enable_notify_events() override
+ {
+ GtkInstanceContainer::enable_notify_events();
+
+ g_signal_handler_unblock(m_pIconView, m_nItemActivatedSignalId);
+ g_signal_handler_unblock(m_pIconView, m_nSelectionChangedSignalId);
+ }
+
+ virtual ~GtkInstanceIconView() override
+ {
+ if (m_pSelectionChangeEvent)
+ Application::RemoveUserEvent(m_pSelectionChangeEvent);
+
+ g_signal_handler_disconnect(m_pIconView, m_nItemActivatedSignalId);
+ g_signal_handler_disconnect(m_pIconView, m_nSelectionChangedSignalId);
+ }
+};
+
+IMPL_LINK_NOARG(GtkInstanceIconView, async_signal_selection_changed, void*, void)
+{
+ m_pSelectionChangeEvent = nullptr;
+ signal_selection_changed();
+}
+
class GtkInstanceSpinButton : public GtkInstanceEntry, public virtual weld::SpinButton
{
private:
@@ -11118,7 +11559,7 @@ public:
bodge_wayland_menu_not_appearing();
}
- virtual void insert_separator(int pos) override
+ virtual void insert_separator(int pos, const OUString& rId) override
{
disable_notify_events();
GtkTreeIter iter;
@@ -11126,7 +11567,7 @@ public:
m_aSeparatorRows.push_back(pos);
if (!gtk_combo_box_get_row_separator_func(m_pComboBox))
gtk_combo_box_set_row_separator_func(m_pComboBox, separatorFunction, this, nullptr);
- insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, nullptr, "", nullptr, nullptr);
+ insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, &rId, "", nullptr, nullptr);
enable_notify_events();
bodge_wayland_menu_not_appearing();
}
@@ -11491,7 +11932,7 @@ public:
m_nEntryInsertTextSignalId = g_signal_connect(pWidget, "insert-text", G_CALLBACK(signalEntryInsertText), this);
}
- virtual void insert_separator(int /*pos*/) override
+ virtual void insert_separator(int /*pos*/, const OUString& /*rId*/) override
{
assert(false);
}
@@ -12262,6 +12703,15 @@ public:
return std::make_unique<GtkInstanceTreeView>(pTreeView, this, bTakeOwnership);
}
+ virtual std::unique_ptr<weld::IconView> weld_icon_view(const OString &id, bool bTakeOwnership) override
+ {
+ GtkIconView* pIconView = GTK_ICON_VIEW(gtk_builder_get_object(m_pBuilder, id.getStr()));
+ if (!pIconView)
+ return nullptr;
+ auto_add_parentless_widgets_to_container(GTK_WIDGET(pIconView));
+ return std::make_unique<GtkInstanceIconView>(pIconView, this, bTakeOwnership);
+ }
+
virtual std::unique_ptr<weld::EntryTreeView> weld_entry_tree_view(const OString& containerid, const OString& entryid, const OString& treeviewid, bool bTakeOwnership) override
{
GtkContainer* pContainer = GTK_CONTAINER(gtk_builder_get_object(m_pBuilder, containerid.getStr()));