diff options
author | Caolán McNamara <caolanm@redhat.com> | 2019-10-21 14:20:12 +0100 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2019-10-29 13:58:47 +0100 |
commit | 09e3d45cdc5c739e5246388a83ccfc6d76bf66e9 (patch) | |
tree | 5ae533f38a626016951b02e7d2406c368b298723 /vcl | |
parent | 93a641d291adf86491cc68ac64f4f614c937183a (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.mk | 2 | ||||
-rw-r--r-- | vcl/inc/iconview.hxx | 39 | ||||
-rw-r--r-- | vcl/inc/iconviewimpl.hxx | 68 | ||||
-rw-r--r-- | vcl/inc/treeglue.hxx | 24 | ||||
-rw-r--r-- | vcl/source/app/salvtables.cxx | 313 | ||||
-rw-r--r-- | vcl/source/treelist/iconview.cxx | 224 | ||||
-rw-r--r-- | vcl/source/treelist/iconviewimpl.cxx | 662 | ||||
-rw-r--r-- | vcl/source/window/builder.cxx | 24 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkinst.cxx | 496 |
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())); |