/* -*- 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/SidebarController.hxx>
// Get the dispatch object as preparation to listen for changes of // the read-only state. const util::URL aURL (Tools::GetURL(gsReadOnlyCommandName));
instance->mxReadOnlyModeDispatch = Tools::GetDispatch(rxFrame, aURL); if (instance->mxReadOnlyModeDispatch.is())
instance->mxReadOnlyModeDispatch->addStatusListener(instance, aURL);
//first UpdateConfigurations call will SwitchToDeck
return instance;
}
SidebarController::~SidebarController()
{
}
SidebarController* SidebarController::GetSidebarControllerForFrame ( const css::uno::Reference<css::frame::XFrame>& rxFrame)
{
uno::Reference<frame::XController> const xController(rxFrame->getController()); if (!xController.is()) // this may happen during dispose of Draw controller but perhaps it's a bug
{
SAL_WARN("sfx.sidebar", "GetSidebarControllerForFrame: frame has no XController"); return nullptr;
}
uno::Reference<ui::XContextChangeEventListener> const xListener(
framework::GetFirstListenerWith(
::comphelper::getProcessComponentContext(),
xController,
[] (uno::Reference<uno::XInterface> const& xRef)
{ return nullptr != dynamic_cast<SidebarController*>(xRef.get()); }
));
// Update to the requested new context asynchronously to avoid // subtle errors caused by SFX2 which in rare cases can not // properly handle a synchronous update.
if (maRequestedContext != maCurrentContext)
{
mxCurrentController.set(rEvent.Source, css::uno::UNO_QUERY);
maContextChangeUpdate.RequestCall(); // async call, not a prob // calling with held // solarmutex
bool bSwitchedApp = maRequestedContext.msApplication != maCurrentContext.msApplication; // Happens on reattach of sidebar to frame or context change // LOK performance impact: prevents to switch sidebar on every keypress in multi user case // Allow when enters embedded OLE (eg. Math formula editor second time) if (!comphelper::LibreOfficeKit::isActive() || bSwitchedApp)
UpdateConfigurations();
}
}
if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
{
SfxSplitWindow* pSplitWindow = GetSplitWindow();
WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right;
tools::Long nDeckX, nTabX; if (eAlign == WindowAlign::Left) // attach the Sidebar towards the left-side of screen
{
nDeckX = nTabBarDefaultWidth;
nTabX = 0;
} else// attach the Sidebar towards the right-side of screen
{
nDeckX = 0;
nTabX = nWidth - nTabBarDefaultWidth;
}
// Place the deck first. if (bIsDeckVisible)
{ if (comphelper::LibreOfficeKit::isActive())
{ // We want to let the layouter use up as much of the // height as necessary to make sure no scrollbar is // visible. This only works when there are no greedy // panes that fill up all available area. So we only // use this for the PropertyDeck, which has no such // panes, while most other do. This is fine, since // it's the PropertyDeck that really has many panes // that can collapse or expand. For others, limit // the height to something sensible. const sal_Int32 nExtHeight = (msCurrentDeckId == "PropertyDeck" ? 2000 : 600); // No TabBar in LOK (use nWidth in full).
mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth, nExtHeight);
} else
mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth - nTabBarDefaultWidth, nHeight);
mpCurrentDeck->Show();
mpCurrentDeck->RequestLayout();
mpTabBar->HighlightDeck(mpCurrentDeck->GetId());
} else
mpCurrentDeck->Hide();
// Now place the tab bar.
mpTabBar->setPosSizePixel(nTabX, 0, nTabBarDefaultWidth, nHeight); if (!comphelper::LibreOfficeKit::isActive())
mpTabBar->Show(); // Don't show TabBar in LOK.
}
// Determine if the closer of the deck can be shown.
sal_Int32 nMinimalWidth = 0; if (mpCurrentDeck && !mpCurrentDeck->isDisposed())
{
DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar(); if (pTitleBar && pTitleBar->GetVisible())
pTitleBar->SetCloserVisible(CanModifyChildWindowWidth());
nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0;
}
if (*mbIsDeckRequestedOpen)
{ // Deck became large enough to be shown. Show it.
mnSavedSidebarWidth = nNewWidth; // Store nNewWidth to mnWidthOnSplitterButtonDown when dragging sidebar Splitter
mnWidthOnSplitterButtonDown = nNewWidth; if (!*mbIsDeckOpen)
RequestOpenDeck();
} else
{ // Deck became too small. Close it completely. // If window is wider than the tab bar then mark the deck as being visible, even when it is not. // This is to trigger an adjustment of the width to the width of the tab bar.
mbIsDeckOpen = true;
RequestCloseDeck();
if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth())
mnSavedSidebarWidth = mnWidthOnSplitterButtonDown;
}
}
// get last active deck for this application on first update if (!maRequestedContext.msApplication.isEmpty() &&
(maCurrentContext.msApplication != maRequestedContext.msApplication))
{ if (bIsLOK)
{ // LOK has no last-used memory constauto& rOverrides = mpResourceManager->GetDeckOverrides(); constauto aOverride = rOverrides.find(maRequestedContext.msApplication); if (aOverride != rOverrides.end())
msCurrentDeckId = aOverride->second;
} else
{
OUString sLastActiveDeck = mpResourceManager->GetLastActiveDeck( maRequestedContext ); if (!sLastActiveDeck.isEmpty())
msCurrentDeckId = sLastActiveDeck;
}
}
// Notify the tab bar about the updated set of decks.
mpTabBar->SetDecks(aDecks);
// Find the new deck. By default that is the same as the old // one. If that is not set or not enabled, then choose the // first enabled deck (which is PropertyDeck).
OUString sNewDeckId; for (constauto& rDeck : aDecks)
{ if (rDeck.mbIsEnabled)
{ if (rDeck.msId == msCurrentDeckId)
{
sNewDeckId = msCurrentDeckId; break;
} elseif (sNewDeckId.getLength() == 0)
sNewDeckId = rDeck.msId;
}
}
if (sNewDeckId.getLength() == 0)
{ // We did not find a valid deck.
RequestCloseDeck(); return;
}
void SidebarController::OpenThenToggleDeck ( const OUString& rsDeckId)
{
SfxSplitWindow* pSplitWindow = GetSplitWindow(); if ( pSplitWindow && !pSplitWindow->IsFadeIn() ) // tdf#83546 Collapsed sidebar should expand first
pSplitWindow->FadeIn(); elseif ( IsDeckVisible( rsDeckId ) )
{ if( !WasFloatingDeckClosed() )
{ // tdf#88241 Summoning an undocked sidebar a second time should close sidebar
mpParentWindow->Close(); return;
} else
{ // tdf#67627 Clicking a second time on a Deck icon will close the Deck
RequestCloseDeck(); return;
}
}
RequestOpenDeck(); // before SwitchToDeck which may cause the rsDeckId string to be released
collectUIInformation(rsDeckId);
SwitchToDeck(rsDeckId);
// Make sure the sidebar is wide enough to fit the requested content if (mpCurrentDeck && mpTabBar)
{
sal_Int32 nRequestedWidth = mpCurrentDeck->GetMinimalWidth() + TabBar::GetDefaultWidth(); // if sidebar was dragged if(mnWidthOnSplitterButtonDown > 0 && mnWidthOnSplitterButtonDown > nRequestedWidth){
SetChildWindowWidth(mnWidthOnSplitterButtonDown);
}else{ // tdf#150639 The mnWidthOnSplitterButtonDown is initialized to 0 at program start. // This makes every call to take the else case until the user manually changes the // width, but some decks such as Master Slides have the mnMinimalWidth too low which // makes them too narrow for the content they should display to the user.
SetChildWindowWidth(nRequestedWidth > mnSavedSidebarWidth ? nRequestedWidth
: mnSavedSidebarWidth);
}
}
}
// Determine if the panel can be displayed. constbool bIsPanelVisible (!mbIsDocumentReadOnly || rPanelContexDescriptor.mbShowForReadOnlyDocuments); if ( ! bIsPanelVisible) continue;
auto xOldPanel(pDeck->GetPanel(rPanelContexDescriptor.msId)); if (xOldPanel)
{
xOldPanel->SetLurkMode(false);
aNewPanels[nWriteIndex] = xOldPanel;
xOldPanel->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible);
++nWriteIndex;
} else
{ auto aPanel = CreatePanel(rPanelContexDescriptor.msId,
pDeck->GetPanelParentWindow(),
rPanelContexDescriptor.mbIsInitiallyVisible,
rContext,
pDeck); if (aPanel)
{
aNewPanels[nWriteIndex] = std::move(aPanel);
// Depending on the context we have to change the command // for the "more options" dialog.
PanelTitleBar* pTitleBar = aNewPanels[nWriteIndex]->GetTitleBar(); if (pTitleBar)
{
pTitleBar->SetMoreOptionsCommand(
rPanelContexDescriptor.msMenuCommand,
mxFrame, xController);
}
++nWriteIndex;
}
}
}
// mpCurrentPanels - may miss stuff (?)
aNewPanels.resize(nWriteIndex);
pDeck->ResetPanels(std::move(aNewPanels));
}
if (aPanelContextDescriptors.empty())
{ // There are no panels to be displayed in the current context. if (vcl::EnumContext::GetContextEnum(rContext.msContext) != vcl::EnumContext::Context::Empty)
{ // Switch to the "empty" context and try again.
SwitchToDeck(
rDeckDescriptor,
Context(
rContext.msApplication,
vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Empty))); return;
} else
{ // This is already the "empty" context. Looks like we have // to live with an empty deck.
}
}
if (bForceNewPanels && !bForceNewDeck) // already forced if bForceNewDeck
CreatePanels(rDeckDescriptor.msId, rContext);
if (mpCurrentDeck && mpCurrentDeck != rDeckDescriptor.mpDeck)
mpCurrentDeck->Hide();
mpCurrentDeck.reset(rDeckDescriptor.mpDeck);
if ( ! mpCurrentDeck) return;
#if OSL_DEBUG_LEVEL >= 2 // Show the context name in the deck title bar.
DeckTitleBar* pDebugTitleBar = mpCurrentDeck->GetTitleBar(); if (pDebugTitleBar)
pDebugTitleBar->SetTitle(rDeckDescriptor.msTitle + " (" + maCurrentContext.msContext + ")"); #endif
SfxSplitWindow* pSplitWindow = GetSplitWindow();
sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth();
WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right;
tools::Long nDeckX; if (eAlign == WindowAlign::Left) // attach the Sidebar towards the left-side of screen
{
nDeckX = nTabBarDefaultWidth;
} else// attach the Sidebar towards the right-side of screen
{
nDeckX = 0;
}
// Activate the deck and the new set of panels.
mpCurrentDeck->setPosSizePixel(
nDeckX,
0,
mpParentWindow->GetSizePixel().Width() - nTabBarDefaultWidth,
mpParentWindow->GetSizePixel().Height());
mpCurrentDeck->Show();
mpParentWindow->SetText(rDeckDescriptor.msTitle);
NotifyResize();
// Tell the focus manager about the new panels and tab bar // buttons.
maFocusManager.SetDeck(mpCurrentDeck);
maFocusManager.SetPanels(mpCurrentDeck->GetPanels());
// Create the panel which is the parent window of the UIElement. auto xPanel = std::make_shared<Panel>(
*xPanelDescriptor,
pParentWindow,
bIsInitiallyExpanded,
pDeck,
[this]() { return this->GetCurrentContext(); },
mxFrame);
// Create the XUIElement.
Reference<ui::XUIElement> xUIElement (CreateUIElement(
xPanel->GetElementParentWindow(),
xPanelDescriptor->msImplementationURL,
xPanelDescriptor->mbWantsCanvas,
rContext)); if (xUIElement.is())
{ // Initialize the panel and add it to the active deck.
xPanel->SetUIElement(xUIElement);
} else
{
xPanel.reset();
}
if (mxCurrentController.is())
{
OUString aModule = Tools::GetModuleName(mxCurrentController); if (!aModule.isEmpty())
aCreationArguments.put(u"Module"_ustr, Any(aModule)); // See also sfx2::sidebar::GetFrame. Maybe we should always create // uielements with the Controller's getFrame as the XFrame. For now // set both XFrame and Controller and selectively get the XFrame // from the Controller.
aCreationArguments.put(u"Controller"_ustr, Any(mxCurrentController));
}
IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent&, rEvent, void)
{ if (rEvent.GetWindow() == mpParentWindow)
{ switch (rEvent.GetId())
{ case VclEventId::WindowShow: case VclEventId::WindowResize:
NotifyResize(); break;
case VclEventId::WindowDataChanged: // Force an update of deck and tab bar to reflect // changes in theme (high contrast mode).
Theme::HandleDataChange();
UpdateTitleBarIcons();
mpParentWindow->Invalidate();
mnRequestedForceFlags |= SwitchFlag_ForceNewDeck | SwitchFlag_ForceNewPanels;
maContextChangeUpdate.RequestCall(); break;
case VclEventId::WindowToggleFloating: // make sure the appropriate "Dock" or "Undock" menu entry is shown
mpTabBar->UpdateMenus(); break;
case VclEventId::ObjectDying:
dispose(); break;
case VclEventId::WindowPaint:
SAL_INFO("sfx.sidebar", "Paint"); break;
// Find the set of decks that could be displayed for the new context.
ResourceManager::DeckContextDescriptorContainer aDecks;
mpResourceManager->GetMatchingDecks (
aDecks,
GetCurrentContext(),
IsDocumentReadOnly(),
mxFrame->getController()); // Notify the tab bar about the updated set of decks.
maFocusManager.Clear();
mpTabBar->SetDecks(aDecks);
mpTabBar->HighlightDeck(mpCurrentDeck->GetId());
mpTabBar->UpdateFocusManager(maFocusManager);
}
mpParentWindow->GrabFocusToDocument();
} catch (RuntimeException&)
{
}
}
// Update (change) the open state when it either has not yet been initialized // or when its value differs from the requested state. if ( mbIsDeckOpen.has_value() && *mbIsDeckOpen == *mbIsDeckRequestedOpen ) return;
if (*mbIsDeckRequestedOpen)
{ if (!mpParentWindow->IsFloatingMode())
{ if (mnSavedSidebarWidth <= nTabBarDefaultWidth)
SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow)); else
SetChildWindowWidth(mnSavedSidebarWidth);
} else
{ // Show the Deck by resizing back to the original size (before hiding).
Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel());
Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel());
if (comphelper::LibreOfficeKit::isActive())
{ // Sidebar wide enough to render the menu; enable it.
mpTabBar->EnableMenuButton(true);
if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell())
{ const std::string uno = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext()); if (!uno.empty())
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
OString(uno + "=true"));
}
}
}
} else
{ if ( ! mpParentWindow->IsFloatingMode())
mnSavedSidebarWidth = SetChildWindowWidth(nTabBarDefaultWidth); else
{ // Hide the Deck by resizing to the width of the TabBar.
Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel());
Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel());
mnSavedSidebarWidth = aNewSize.Width(); // Save the current width to restore.
aNewPos.setX(aNewPos.X() + mnSavedSidebarWidth - nTabBarDefaultWidth); if (comphelper::LibreOfficeKit::isActive())
{ // Hide by collapsing, otherwise with 0x0 the client might expect // to get valid dimensions on rendering and not collapse the sidebar.
aNewSize.setWidth(1);
} else
aNewSize.setWidth(nTabBarDefaultWidth);
void SidebarController::UpdateCloseIndicator (constbool bCloseAfterDrag)
{ if (mpParentWindow == nullptr) return;
if (bCloseAfterDrag)
{ // Make sure that the indicator exists. if (!mpCloseIndicator)
mpCloseIndicator.reset(VclPtr<CloseIndicator>::Create(mpParentWindow));
// Place and show the indicator. const Size aWindowSize (mpParentWindow->GetSizePixel()); const Size aImageSize (mpCloseIndicator->GetSizePixel());
mpCloseIndicator->SetPosPixel(
Point(
aWindowSize.Width() - TabBar::GetDefaultWidth() - aImageSize.Width(),
(aWindowSize.Height() - aImageSize.Height())/2));
mpCloseIndicator->Show();
} else
{ // Hide but don't delete the indicator. if (mpCloseIndicator)
mpCloseIndicator->Hide();
}
}
void SidebarController::UpdateTitleBarIcons()
{ if ( ! mpCurrentDeck) return;
void SidebarController::saveDeckState()
{ // Impress shutdown : context (frame) is disposed before sidebar disposing // calc writer : context (frame) is disposed after sidebar disposing // so need to test if GetCurrentContext is still valid regarding msApplication if (GetCurrentContext().msApplication != "none")
{
mpResourceManager->SaveDecksSettings(GetCurrentContext());
mpResourceManager->SaveLastActiveDeck(GetCurrentContext(), msCurrentDeckId);
}
}
bool SidebarController::hasChartOrMathContextCurrently() const
{ if ((maRequestedContext != maCurrentContext) && isChartOrMathContext(maRequestedContext)) returntrue; // We are not yet changed, but in the process
return isChartOrMathContext(maCurrentContext);
}
sfx2::sidebar::SidebarController* SidebarController::GetSidebarControllerForView(const SfxViewShell* pViewShell)
{ if (!pViewShell) return nullptr;
Reference<css::frame::XController2> xController(pViewShell->GetController(), UNO_QUERY); if (!xController.is()) return nullptr;
// Make sure there is a model behind the controller, otherwise getSidebar() can crash. if (!xController->getModel().is()) return nullptr;
Reference<css::ui::XSidebarProvider> xSidebarProvider = xController->getSidebar(); if (!xSidebarProvider.is()) return nullptr;
Reference<css::ui::XSidebar> xSidebar = xSidebarProvider->getSidebar(); if (!xSidebar.is()) return nullptr;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.