/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace css; using namespace css::uno; namespace sfx2 { namespace sidebar { Deck::Deck(const DeckDescriptor& rDeckDescriptor, vcl::Window* pParentWindow, const std::function& rCloserAction) : Window(pParentWindow, 0) , msId(rDeckDescriptor.msId) , mnMinimalWidth(0) , mnMinimalHeight(0) , maPanels() , mpTitleBar(VclPtr::Create(rDeckDescriptor.msTitle, this, rCloserAction)) , mpScrollClipWindow(VclPtr::Create(this)) , mpScrollContainer(VclPtr::Create(mpScrollClipWindow.get())) , mpFiller(VclPtr::Create(this)) , mpVerticalScrollBar(VclPtr::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 & 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 const & Deck::GetTitleBar() const { return mpTitleBar; } tools::Rectangle Deck::GetContentArea() const { const Size aWindowSize (GetSizePixel()); const int nBorderSize (Theme::GetInteger(Theme::Int_DeckBorderSize)); 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(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 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 aChildren; for (auto &it : maPanels) { if (it->IsLurking()) continue; // collapse the panel itself out auto xContent = it->GetElementWindow(); if (!xContent.is()) continue; VclPtr pWindow = VCLUnoHelper::GetWindow(xContent); if (!pWindow) continue; boost::property_tree::ptree aChild = pWindow->DumpAsPropertyTree(); aChild.put("text", it->GetText()); aChild.put("type", "panel"); aChildren.push_back(std::make_pair("", aChild)); } aTree.add_child("children", aChildren); 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 & 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; } if (mnMinimalWidth > 0 && (mnMinimalWidth != aParentSize.Width() || GetSizePixel().Width() != mnMinimalWidth) && comphelper::LibreOfficeKit::isMobile(SfxLokHelper::getView())) { 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 & 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& rSeparators) { maSeparators = rSeparators; Invalidate(); } } } // end of namespace sfx2::sidebar /* vim:set shiftwidth=4 softtabstop=4 expandtab: */