summaryrefslogtreecommitdiffstats
path: root/sfx2/source/sidebar/Deck.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/sidebar/Deck.cxx')
-rw-r--r--sfx2/source/sidebar/Deck.cxx447
1 files changed, 447 insertions, 0 deletions
diff --git a/sfx2/source/sidebar/Deck.cxx b/sfx2/source/sidebar/Deck.cxx
new file mode 100644
index 000000000..d96305c3e
--- /dev/null
+++ b/sfx2/source/sidebar/Deck.cxx
@@ -0,0 +1,447 @@
+/* -*- 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 <sfx2/sidebar/Deck.hxx>
+#include <sidebar/DeckDescriptor.hxx>
+#include <sidebar/DeckLayouter.hxx>
+#include <sidebar/DrawHelper.hxx>
+#include <sidebar/DeckTitleBar.hxx>
+#include <sidebar/PanelTitleBar.hxx>
+#include <sidebar/Paint.hxx>
+#include <sfx2/sidebar/Panel.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+#include <sfx2/lokhelper.hxx>
+
+#include <vcl/event.hxx>
+#include <comphelper/lok.hxx>
+#include <vcl/scrbar.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/IDialogRenderable.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/svborder.hxx>
+#include <boost/property_tree/ptree.hpp>
+#include <sal/log.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace sfx2::sidebar {
+
+Deck::Deck(const DeckDescriptor& rDeckDescriptor, vcl::Window* pParentWindow,
+ const std::function<void()>& rCloserAction)
+ : Window(pParentWindow, 0)
+ , msId(rDeckDescriptor.msId)
+ , mnMinimalWidth(0)
+ , mnMinimalHeight(0)
+ , maPanels()
+ , mpTitleBar(VclPtr<DeckTitleBar>::Create(rDeckDescriptor.msTitle, this, rCloserAction))
+ , mpScrollClipWindow(VclPtr<vcl::Window>::Create(this))
+ , mpScrollContainer(VclPtr<ScrollContainerWindow>::Create(mpScrollClipWindow.get()))
+ , mpFiller(VclPtr<vcl::Window>::Create(this))
+ , mpVerticalScrollBar(VclPtr<ScrollBar>::Create(this))
+{
+ mpScrollClipWindow->SetBackground(Wallpaper());
+ mpScrollClipWindow->Show();
+
+ mpScrollContainer->SetStyle(mpScrollContainer->GetStyle() | WB_DIALOGCONTROL);
+ mpScrollContainer->SetBackground(Wallpaper());
+ mpScrollContainer->Show();
+
+ mpVerticalScrollBar->SetScrollHdl(LINK(this, Deck, HandleVerticalScrollBarChange));
+ mpVerticalScrollBar->SetLineSize(10);
+ mpVerticalScrollBar->SetPageSize(100);
+
+#ifdef DEBUG
+ SetText(OUString("Deck"));
+ mpScrollClipWindow->SetText(OUString("ScrollClipWindow"));
+ mpFiller->SetText(OUString("Filler"));
+ mpVerticalScrollBar->SetText(OUString("VerticalScrollBar"));
+#endif
+}
+
+Deck::~Deck()
+{
+ disposeOnce();
+}
+
+void Deck::dispose()
+{
+ SharedPanelContainer aPanels;
+ aPanels.swap(maPanels);
+
+ // We have to explicitly trigger the destruction of panels.
+ // Otherwise that is done by one of our base class destructors
+ // without updating maPanels.
+ for (VclPtr<Panel> & rpPanel : aPanels)
+ rpPanel.disposeAndClear();
+
+ maPanels.clear(); // just to keep the loplugin:vclwidgets happy
+ mpTitleBar.disposeAndClear();
+ mpFiller.disposeAndClear();
+ mpVerticalScrollBar.disposeAndClear();
+ mpScrollContainer.disposeAndClear();
+ mpScrollClipWindow.disposeAndClear();
+
+ vcl::Window::dispose();
+}
+
+VclPtr<DeckTitleBar> const & Deck::GetTitleBar() const
+{
+ return mpTitleBar;
+}
+
+tools::Rectangle Deck::GetContentArea() const
+{
+ const Size aWindowSize (GetSizePixel());
+ const int nBorderSize (Theme::GetInteger(Theme::Int_DeckBorderSize));
+ if (aWindowSize.IsEmpty())
+ return tools::Rectangle();
+
+ return tools::Rectangle(
+ Theme::GetInteger(Theme::Int_DeckLeftPadding) + nBorderSize,
+ Theme::GetInteger(Theme::Int_DeckTopPadding) + nBorderSize,
+ aWindowSize.Width() - 1 - Theme::GetInteger(Theme::Int_DeckRightPadding) - nBorderSize,
+ aWindowSize.Height() - 1 - Theme::GetInteger(Theme::Int_DeckBottomPadding) - nBorderSize);
+}
+
+void Deck::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ rRenderContext.SetBackground(Wallpaper());
+}
+
+void Deck::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rUpdateArea*/)
+{
+ const Size aWindowSize (GetSizePixel());
+ const SvBorder aPadding(Theme::GetInteger(Theme::Int_DeckLeftPadding),
+ Theme::GetInteger(Theme::Int_DeckTopPadding),
+ Theme::GetInteger(Theme::Int_DeckRightPadding),
+ Theme::GetInteger(Theme::Int_DeckBottomPadding));
+
+ // Paint deck background outside the border.
+ tools::Rectangle aBox(0, 0, aWindowSize.Width() - 1, aWindowSize.Height() - 1);
+ DrawHelper::DrawBorder(rRenderContext, aBox, aPadding,
+ Theme::GetPaint(Theme::Paint_DeckBackground),
+ Theme::GetPaint(Theme::Paint_DeckBackground));
+
+ // Paint the border.
+ const int nBorderSize(Theme::GetInteger(Theme::Int_DeckBorderSize));
+ aBox.AdjustLeft(aPadding.Left() );
+ aBox.AdjustTop(aPadding.Top() );
+ aBox.AdjustRight( -(aPadding.Right()) );
+ aBox.AdjustBottom( -(aPadding.Bottom()) );
+ const sfx2::sidebar::Paint& rHorizontalBorderPaint(Theme::GetPaint(Theme::Paint_HorizontalBorder));
+ DrawHelper::DrawBorder(rRenderContext, aBox,
+ SvBorder(nBorderSize, nBorderSize, nBorderSize, nBorderSize),
+ rHorizontalBorderPaint,
+ Theme::GetPaint(Theme::Paint_VerticalBorder));
+}
+
+void Deck::DataChanged (const DataChangedEvent&)
+{
+ RequestLayoutInternal();
+}
+
+bool Deck::EventNotify(NotifyEvent& rEvent)
+{
+ if (rEvent.GetType() == MouseNotifyEvent::COMMAND)
+ {
+ CommandEvent* pCommandEvent = static_cast<CommandEvent*>(rEvent.GetData());
+ if (pCommandEvent != nullptr)
+ switch (pCommandEvent->GetCommand())
+ {
+ case CommandEventId::Wheel:
+ return ProcessWheelEvent(pCommandEvent);
+
+ default:
+ break;
+ }
+ }
+
+ return Window::EventNotify(rEvent);
+}
+
+void Deck::Resize()
+{
+ Window::Resize();
+
+ if (const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
+ {
+ std::vector<vcl::LOKPayloadItem> aItems;
+ aItems.emplace_back("type", "deck");
+ aItems.emplace_back(std::make_pair("position", Point(GetOutOffXPixel(), GetOutOffYPixel()).toString()));
+ aItems.emplace_back(std::make_pair("size", GetSizePixel().toString()));
+ pNotifier->notifyWindow(GetLOKWindowId(), "size_changed", aItems);
+ }
+}
+
+/*
+ * Get the ordering as is shown in the layout, and our type as 'deck'
+ * also elide nested panel windows.
+ */
+boost::property_tree::ptree Deck::DumpAsPropertyTree()
+{
+ boost::property_tree::ptree aTree;
+ aTree.put("id", get_id()); // TODO could be missing - sort out
+ aTree.put("type", "deck");
+ aTree.put("text", GetText());
+ aTree.put("enabled", IsEnabled());
+
+ boost::property_tree::ptree aPanelNodes;
+ for (auto &it : maPanels)
+ {
+ if (it->IsLurking())
+ continue;
+
+ // collapse the panel itself out
+ auto xContent = it->GetElementWindow();
+ if (!xContent.is())
+ continue;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xContent);
+ if (!pWindow)
+ continue;
+
+ boost::property_tree::ptree aPanel;
+ aPanel.put("id", it->GetId());
+ aPanel.put("type", "panel");
+ aPanel.put("text", it->GetText());
+ aPanel.put("enabled", it->IsEnabled());
+
+ boost::property_tree::ptree aChildren;
+ aChildren.push_back(std::make_pair("", pWindow->DumpAsPropertyTree()));
+ aPanel.add_child("children", aChildren);
+
+ aPanelNodes.push_back(std::make_pair("", aPanel));
+ }
+ aTree.add_child("children", aPanelNodes);
+
+ return aTree;
+}
+
+bool Deck::ProcessWheelEvent(CommandEvent const * pCommandEvent)
+{
+ if ( ! mpVerticalScrollBar)
+ return false;
+ if ( ! mpVerticalScrollBar->IsVisible())
+ return false;
+
+ // Get the wheel data and check that it describes a valid vertical
+ // scroll.
+ const CommandWheelData* pData = pCommandEvent->GetWheelData();
+ if (pData==nullptr
+ || pData->GetModifier()
+ || pData->GetMode() != CommandWheelMode::SCROLL
+ || pData->IsHorz())
+ return false;
+
+ // Execute the actual scroll action.
+ long nDelta = pData->GetDelta();
+ mpVerticalScrollBar->DoScroll(
+ mpVerticalScrollBar->GetThumbPos() - nDelta);
+ return true;
+}
+
+/**
+ * This container may contain existing panels that are
+ * being re-used, and new ones too.
+ */
+void Deck::ResetPanels(const SharedPanelContainer& rPanelContainer)
+{
+ SharedPanelContainer aHiddens;
+
+ // First hide old panels we don't need just now.
+ for (VclPtr<Panel> & rpPanel : maPanels)
+ {
+ bool bFound = false;
+ for (const auto & i : rPanelContainer)
+ bFound = bFound || (rpPanel.get() == i.get());
+ if (!bFound) // this one didn't survive.
+ {
+ rpPanel->SetLurkMode(true);
+ aHiddens.push_back(rpPanel);
+ }
+ }
+ maPanels = rPanelContainer;
+
+ // Hidden ones always at the end
+ maPanels.insert(std::end(maPanels), std::begin(aHiddens), std::end(aHiddens));
+
+ RequestLayoutInternal();
+}
+
+void Deck::RequestLayoutInternal()
+{
+ mnMinimalWidth = 0;
+ mnMinimalHeight = 0;
+
+ DeckLayouter::LayoutDeck(GetContentArea(), mnMinimalWidth, mnMinimalHeight, maPanels,
+ *GetTitleBar(), *mpScrollClipWindow, *mpScrollContainer,
+ *mpFiller, *mpVerticalScrollBar);
+}
+
+void Deck::RequestLayout()
+{
+ RequestLayoutInternal();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bool bChangeNeeded = false;
+ Size aParentSize = GetParent()->GetSizePixel();
+
+ if (mnMinimalHeight > 0 && (mnMinimalHeight != aParentSize.Height() || GetSizePixel().Height() != mnMinimalHeight))
+ {
+ aParentSize.setHeight(mnMinimalHeight);
+ bChangeNeeded = true;
+ }
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (mnMinimalWidth > 0 && (mnMinimalWidth != aParentSize.Width() || GetSizePixel().Width() != mnMinimalWidth)
+ && pViewShell && pViewShell->isLOKMobilePhone())
+ {
+ aParentSize.setWidth(mnMinimalWidth);
+ bChangeNeeded = true;
+ }
+
+ if (bChangeNeeded)
+ {
+ GetParent()->SetSizePixel(aParentSize);
+ setPosSizePixel(0, 0, aParentSize.Width(), aParentSize.Height());
+ }
+ else if (aParentSize != GetSizePixel()) //Sync parent & child sizes
+ setPosSizePixel(0, 0, aParentSize.Width(), aParentSize.Height());
+ }
+}
+
+vcl::Window* Deck::GetPanelParentWindow()
+{
+ return mpScrollContainer.get();
+}
+
+Panel* Deck::GetPanel(const OUString & panelId)
+{
+ for (const VclPtr<Panel> & pPanel : maPanels)
+ {
+ if(pPanel->GetId() == panelId)
+ {
+ return pPanel.get();
+ }
+ }
+ return nullptr;
+
+}
+
+void Deck::ShowPanel(const Panel& rPanel)
+{
+ if (!mpVerticalScrollBar || !mpVerticalScrollBar->IsVisible())
+ return;
+
+ // Get vertical extent of the panel.
+ sal_Int32 nPanelTop (rPanel.GetPosPixel().Y());
+ const sal_Int32 nPanelBottom (nPanelTop + rPanel.GetSizePixel().Height() - 1);
+ // Add the title bar into the extent.
+ if (rPanel.GetTitleBar() && rPanel.GetTitleBar()->IsVisible())
+ nPanelTop = rPanel.GetTitleBar()->GetPosPixel().Y();
+
+ // Determine what the new thumb position should be like.
+ // When the whole panel does not fit then make its top visible
+ // and it off at the bottom.
+ sal_Int32 nNewThumbPos (mpVerticalScrollBar->GetThumbPos());
+ if (nPanelBottom >= nNewThumbPos+mpVerticalScrollBar->GetVisibleSize())
+ nNewThumbPos = nPanelBottom - mpVerticalScrollBar->GetVisibleSize();
+ if (nPanelTop < nNewThumbPos)
+ nNewThumbPos = nPanelTop;
+
+ mpVerticalScrollBar->SetThumbPos(nNewThumbPos);
+ mpScrollContainer->SetPosPixel(
+ Point(
+ mpScrollContainer->GetPosPixel().X(),
+ -nNewThumbPos));
+}
+
+static OUString GetWindowClassification(const vcl::Window* pWindow)
+{
+ const OUString& rsName (pWindow->GetText());
+ if (!rsName.isEmpty())
+ {
+ return rsName;
+ }
+ else
+ {
+ return "window";
+ }
+}
+
+void Deck::PrintWindowSubTree(vcl::Window* pRoot, int nIndentation)
+{
+ static const char* const sIndentation = " ";
+ const Point aLocation (pRoot->GetPosPixel());
+ const Size aSize (pRoot->GetSizePixel());
+ SAL_INFO(
+ "sfx.sidebar",
+ sIndentation + strlen(sIndentation) - nIndentation * 4 << pRoot << " "
+ << GetWindowClassification(pRoot) << " "
+ << (pRoot->IsVisible() ? "visible" : "hidden") << " +"
+ << aLocation.X() << "+" << aLocation.Y() << " x" << aSize.Width()
+ << "x" << aSize.Height());
+
+ const sal_uInt16 nChildCount(pRoot->GetChildCount());
+ for (sal_uInt16 nIndex = 0; nIndex < nChildCount; ++nIndex)
+ PrintWindowSubTree(pRoot->GetChild(nIndex), nIndentation + 1);
+}
+
+IMPL_LINK_NOARG(Deck, HandleVerticalScrollBarChange, ScrollBar*, void)
+{
+ const sal_Int32 nYOffset (-mpVerticalScrollBar->GetThumbPos());
+ mpScrollContainer->SetPosPixel(Point(mpScrollContainer->GetPosPixel().X(),
+ nYOffset));
+ mpScrollContainer->Invalidate();
+}
+
+//----- Deck::ScrollContainerWindow -------------------------------------------
+
+Deck::ScrollContainerWindow::ScrollContainerWindow (vcl::Window* pParentWindow)
+ : Window(pParentWindow),
+ maSeparators()
+{
+#ifdef DEBUG
+ SetText(OUString("ScrollContainerWindow"));
+#endif
+}
+
+void Deck::ScrollContainerWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rUpdateArea*/)
+{
+ // Paint the separators.
+ const sal_Int32 nSeparatorHeight(Theme::GetInteger(Theme::Int_DeckSeparatorHeight));
+ const sal_Int32 nLeft(0);
+ const sal_Int32 nRight(GetSizePixel().Width() - 1);
+ const sfx2::sidebar::Paint& rHorizontalBorderPaint(Theme::GetPaint(Theme::Paint_HorizontalBorder));
+ for (auto const& separator : maSeparators)
+ {
+ DrawHelper::DrawHorizontalLine(rRenderContext, nLeft, nRight, separator,
+ nSeparatorHeight, rHorizontalBorderPaint);
+ }
+}
+
+void Deck::ScrollContainerWindow::SetSeparators (const ::std::vector<sal_Int32>& rSeparators)
+{
+ maSeparators = rSeparators;
+ Invalidate();
+}
+
+} // end of namespace sfx2::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */